Skip to content

Commit

Permalink
feat(tree): add theming for tree component
Browse files Browse the repository at this point in the history
RISDEV-4997
  • Loading branch information
hamo225 committed Oct 22, 2024
1 parent a50e25a commit 2688531
Show file tree
Hide file tree
Showing 4 changed files with 280 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/primevue/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import toast from "./toast/toast";
import select from "./select/select";
import inputMask from "./inputMask/inputMask";
import breadcrumb from "@/primevue/breadcrumb/breadcrumb";
import tree from "./tree/tree";

import { deDE } from "@/config/locale";

Expand All @@ -40,6 +41,7 @@ export const RisUiTheme = {
select,
inputMask,
breadcrumb,
tree,
};

export const RisUiLocale = {
Expand Down
16 changes: 16 additions & 0 deletions src/primevue/tree/tree.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
span[data-pc-section="nodelabel"]:has(a + span) {
@apply gap-4;
}

span[data-pc-section="nodelabel"] a,
span[data-pc-section="nodelabel"] span {
@apply w-full focus:outline-none;
}

span[data-pc-section="nodelabel"] :first-child {
@apply group-hover:underline;
}

span[data-pc-section="nodelabel"] span:last-child {
@apply group-hover:no-underline;
}
200 changes: 200 additions & 0 deletions src/primevue/tree/tree.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import { Meta, StoryObj } from "@storybook/vue3";
import Tree from "primevue/tree";
import { html } from "@/lib/tags.ts";
import { ref, onMounted } from "vue";
import ChevronDownIcon from "~icons/mdi/chevron-down";
import ChevronUpIcon from "~icons/mdi/chevron-up";
import PrimevueButton from "primevue/button";
import { vueRouter } from "storybook-vue3-router";

interface TreeNode {
key: string;
label: string;
route?: string;
secondaryLabel?: string;
children?: TreeNode[];
}

const meta: Meta<typeof Tree> = {
component: Tree,
tags: ["autodocs"],
args: {},
};

export default meta;
type Story = StoryObj<typeof meta>;

export const Default: Story = {
render: (args) => ({
components: { Tree, ChevronDownIcon, ChevronUpIcon, PrimevueButton },
setup() {
const nodes = ref<TreeNode[]>([]);
const expandedKeys = ref<Record<string, boolean>>({});
const isExpanded = ref<boolean>(false);
const selectionKeys = ref<Record<string, boolean>>({});

onMounted(() => {
nodes.value = [
{
key: "0",
label: "Primary Text",
secondaryLabel: "Secondary Text",
route: "/",
children: [
{
key: "0-0",
label: "Child 1",
secondaryLabel: "Secondary Text",
children: [
{
key: "0-0-0",
label: "Grandchild 1",
secondaryLabel: "Secondary Text",
route: "/grandchild-1",
},
{
key: "0-0-1",
label: "Grandchild 2",
secondaryLabel: "Secondary Text",
route: "/grandchild-2",
children: [
{
key: "0-0-1-0",
label: "Great Grandchild",
route: "/great-grandchild",
},
],
},
{
key: "0-0-1-1",
label: "Grandchild 3",
secondaryLabel: "Secondary Text",
children: [
{
key: "0-0-1-2",
label: "Great Grandchild 2",
route: "/great-grandchild-2",
},
],
},
],
},
{
key: "0-1",
label: "Child 2",
secondaryLabel: "Secondary Text",
route: "/child-2",
children: [
{
key: "0-1-0",
label: "Grandchild 4",
secondaryLabel: "Secondary Text",
route: "/grandchild-4",
},
],
},
],
},
];
});

// Toggle expansion on click
const toggleNode = (node: TreeNode) => {
if (expandedKeys.value[node.key]) {
delete expandedKeys.value[node.key];
} else {
expandedKeys.value[node.key] = true;
}
expandedKeys.value = { ...expandedKeys.value };
};

// Expand all nodes
const expandAll = () => {
const expandNode = (node: TreeNode) => {
expandedKeys.value[node.key] = true;
if (node.children) {
node.children.forEach(expandNode);
}
};

nodes.value.forEach(expandNode);
expandedKeys.value = { ...expandedKeys.value };
};

// Collapse all nodes
const collapseAll = () => {
expandedKeys.value = {};
};

// Toggle expand/collapse for all nodes
const toggleExpandCollapse = () => {
if (isExpanded.value) {
collapseAll();
} else {
expandAll();
}
isExpanded.value = !isExpanded.value;
};

return {
args,
nodes,
expandedKeys,
selectionKeys,
isExpanded,
toggleExpandCollapse,
toggleNode,
};
},
template: html`
<div class="card w-full">
<div
class="mb-6 flex w-full cursor-pointer items-center justify-between gap-2"
@click="toggleExpandCollapse"
>
<span>Inhaltsverzeichnis</span>
<PrimevueButton text label="Alle Ebenen öffnen" v-if="!isExpanded">
<template #icon>
<ChevronDownIcon />
</template>
</PrimevueButton>
<PrimevueButton text label="Alle Ebenen schließen" v-else>
<template #icon>
<ChevronUpIcon />
</template>
</PrimevueButton>
</div>
<Tree
v-model:expandedKeys="expandedKeys"
v-model:selectionKeys="selectionKeys"
:value="nodes"
selectionMode="single"
>
<template #default="{ node, selected, expanded }">
<router-link
v-if="node.route"
:to="node.route"
@click="toggleNode(node)"
>
{{ node.label }}
</router-link>
<span v-else class="w-full" @click="toggleNode(node)"
>{{ node.label }}</span
>
<span class="ris-label2-regular" @click="toggleNode(node)"
>{{ node.secondaryLabel }}</span
>
</template>
<template #nodetoggleicon="{ expanded }">
<ChevronDownIcon v-if="!expanded" />
<ChevronUpIcon v-else />
</template>
</Tree>
</div>
`,
}),
decorators: [
vueRouter(), // This will add basic router functionality to your story
],
};
62 changes: 62 additions & 0 deletions src/primevue/tree/tree.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { TreePassThroughOptions } from "primevue/tree";
import { tw } from "@/lib/tags.ts";
import "./tree.css";

const tree: TreePassThroughOptions = {
node: () => {
const focus = tw`focus-visible:outline-none focus-visible:outline-4 focus-visible:outline-offset-4 focus-visible:outline-blue-800`; // Adding focus state with a red border
return {
class: {
[focus]: true,
},
};
},
nodeContent: ({ context }) => {
const base = tw`group ris-label2-bold flex w-full border-l-4 border-transparent py-10 pl-10 pr-20 text-blue-800 hover:bg-gray-100`;
const pointer = tw`cursor-pointer select-none`;
const focusVisible = tw`focus-visible:outline-none focus-visible:outline-4 focus-visible:outline-offset-4 focus-visible:outline-blue-800`; // Adding focus state with a red border
const selected = tw`border-l-blue-800 bg-gray-100`;
const hoverActive = tw`hover:active:bg-blue-200`;

return {
class: {
[base]: true,
[selected]: context.selected,
[pointer]: true,
[focusVisible]: true,
[hoverActive]: true,
},
};
},
nodeToggleButton: ({ context }) => {
const base = tw`inline-flex h-24 w-24 justify-center border-0 bg-transparent outline-none hover:text-black group-hover:text-black`;
const invisible = tw`invisible`;

return {
class: {
[base]: true,
[invisible]: context.leaf,
},
};
},
nodeChildren: () => {
const base = tw`m-0 ml-14 mt-1 w-full list-none p-0 outline-none`;
const focusVisible = tw`focus-visible:outline-none focus-visible:outline-4 focus-visible:outline-offset-4 focus-visible:outline-blue-800`; // Adding focus state with a red border
return {
class: {
[base]: true,
[focusVisible]: true,
},
};
},
nodeLabel: () => {
const base = tw`group flex w-full flex-col items-start outline-none group-hover:text-black`;
return {
class: {
[base]: true,
},
};
},
};

export default tree;

0 comments on commit 2688531

Please sign in to comment.