fds-vue-core 2.1.4 → 2.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/components.d.ts +8 -0
  2. package/configs/tsconfig.base.json +2 -1
  3. package/dist/fds-vue-core.cjs.js +35 -15
  4. package/dist/fds-vue-core.cjs.js.map +1 -1
  5. package/dist/fds-vue-core.es.js +35 -15
  6. package/dist/fds-vue-core.es.js.map +1 -1
  7. package/dist/global-components.d.ts +35 -33
  8. package/package.json +23 -21
  9. package/src/.DS_Store +0 -0
  10. package/src/App.vue +133 -0
  11. package/src/apply.css +60 -0
  12. package/src/assets/icons.ts +517 -0
  13. package/src/components/Blocks/FdsBlockAlert/FdsBlockAlert.stories.ts +94 -0
  14. package/src/components/Blocks/FdsBlockAlert/FdsBlockAlert.vue +112 -0
  15. package/src/components/Blocks/FdsBlockAlert/types.ts +12 -0
  16. package/src/components/Blocks/FdsBlockContent/FdsBlockContent.stories.ts +110 -0
  17. package/src/components/Blocks/FdsBlockContent/FdsBlockContent.vue +66 -0
  18. package/src/components/Blocks/FdsBlockContent/types.ts +6 -0
  19. package/src/components/Blocks/FdsBlockExpander/FdsBlockExpander.stories.ts +123 -0
  20. package/src/components/Blocks/FdsBlockExpander/FdsBlockExpander.vue +87 -0
  21. package/src/components/Blocks/FdsBlockExpander/types.ts +8 -0
  22. package/src/components/Blocks/FdsBlockInfo/FdsBlockInfo.stories.ts +110 -0
  23. package/src/components/Blocks/FdsBlockInfo/FdsBlockInfo.vue +75 -0
  24. package/src/components/Blocks/FdsBlockInfo/types.ts +9 -0
  25. package/src/components/Blocks/FdsBlockLink/FdsBlockLink.css +9 -0
  26. package/src/components/Blocks/FdsBlockLink/FdsBlockLink.stories.ts +179 -0
  27. package/src/components/Blocks/FdsBlockLink/FdsBlockLink.vue +149 -0
  28. package/src/components/Blocks/FdsBlockLink/types.ts +14 -0
  29. package/src/components/Buttons/ButtonBaseProps.ts +18 -0
  30. package/src/components/Buttons/FdsButtonCopy/FdsButtonCopy.stories.ts +53 -0
  31. package/src/components/Buttons/FdsButtonCopy/FdsButtonCopy.vue +87 -0
  32. package/src/components/Buttons/FdsButtonCopy/types.ts +8 -0
  33. package/src/components/Buttons/FdsButtonDownload/FdsButtonDownload.stories.ts +111 -0
  34. package/src/components/Buttons/FdsButtonDownload/FdsButtonDownload.vue +187 -0
  35. package/src/components/Buttons/FdsButtonIcon/FdsButtonIcon.stories.ts +55 -0
  36. package/src/components/Buttons/FdsButtonIcon/FdsButtonIcon.vue +57 -0
  37. package/src/components/Buttons/FdsButtonIcon/types.ts +12 -0
  38. package/src/components/Buttons/FdsButtonMinor/FdsButtonMinor.stories.ts +68 -0
  39. package/src/components/Buttons/FdsButtonMinor/FdsButtonMinor.vue +126 -0
  40. package/src/components/Buttons/FdsButtonPrimary/FdsButtonPrimary.stories.ts +86 -0
  41. package/src/components/Buttons/FdsButtonPrimary/FdsButtonPrimary.vue +107 -0
  42. package/src/components/Buttons/FdsButtonSecondary/FdsButtonSecondary.stories.ts +68 -0
  43. package/src/components/Buttons/FdsButtonSecondary/FdsButtonSecondary.vue +107 -0
  44. package/src/components/FdsIcon/FdsIcon.stories.ts +69 -0
  45. package/src/components/FdsIcon/FdsIcon.vue +34 -0
  46. package/src/components/FdsIcon/types.ts +9 -0
  47. package/src/components/FdsModal/FdsModal.stories.ts +241 -0
  48. package/src/components/FdsModal/FdsModal.vue +269 -0
  49. package/src/components/FdsModal/types.ts +12 -0
  50. package/src/components/FdsPagination/FdsPagination.stories.ts +109 -0
  51. package/src/components/FdsPagination/FdsPagination.vue +193 -0
  52. package/src/components/FdsPagination/types.ts +6 -0
  53. package/src/components/FdsSearchSelect/FdsSearchSelect.stories.ts +428 -0
  54. package/src/components/FdsSearchSelect/FdsSearchSelect.vue +621 -0
  55. package/src/components/FdsSearchSelect/types.ts +25 -0
  56. package/src/components/FdsSpinner/FdsSpinner.stories.ts +31 -0
  57. package/src/components/FdsSpinner/FdsSpinner.vue +90 -0
  58. package/src/components/FdsSpinner/types.ts +6 -0
  59. package/src/components/FdsSticker/FdsSticker.stories.ts +148 -0
  60. package/src/components/FdsSticker/FdsSticker.vue +44 -0
  61. package/src/components/FdsSticker/types.ts +4 -0
  62. package/src/components/FdsTreeView/FdsTreeView.stories.ts +136 -0
  63. package/src/components/FdsTreeView/FdsTreeView.vue +162 -0
  64. package/src/components/FdsTreeView/TreeNode.vue +383 -0
  65. package/src/components/FdsTreeView/types.ts +141 -0
  66. package/src/components/FdsTreeView/useTreeState.ts +607 -0
  67. package/src/components/FdsTreeView/utils.ts +69 -0
  68. package/src/components/FdsTruncatedText/FdsTruncatedText.stories.ts +78 -0
  69. package/src/components/FdsTruncatedText/FdsTruncatedText.vue +85 -0
  70. package/src/components/FdsTruncatedText/types.ts +6 -0
  71. package/src/components/Form/FdsCheckbox/FdsCheckbox.stories.ts +275 -0
  72. package/src/components/Form/FdsCheckbox/FdsCheckbox.vue +155 -0
  73. package/src/components/Form/FdsCheckbox/types.ts +10 -0
  74. package/src/components/Form/FdsInput/FdsInput.stories.ts +319 -0
  75. package/src/components/Form/FdsInput/FdsInput.vue +233 -0
  76. package/src/components/Form/FdsInput/types.ts +25 -0
  77. package/src/components/Form/FdsRadio/FdsRadio.stories.ts +63 -0
  78. package/src/components/Form/FdsRadio/FdsRadio.vue +88 -0
  79. package/src/components/Form/FdsRadio/types.ts +12 -0
  80. package/src/components/Form/FdsSelect/FdsSelect.stories.ts +78 -0
  81. package/src/components/Form/FdsSelect/FdsSelect.vue +136 -0
  82. package/src/components/Form/FdsSelect/types.ts +13 -0
  83. package/src/components/Form/FdsTextarea/FdsTextarea.stories.ts +52 -0
  84. package/src/components/Form/FdsTextarea/FdsTextarea.vue +110 -0
  85. package/src/components/Form/FdsTextarea/types.ts +12 -0
  86. package/src/components/Table/FdsTable/FdsTable.stories.ts +221 -0
  87. package/src/components/Table/FdsTable/FdsTable.vue +25 -0
  88. package/src/components/Table/FdsTable/types.ts +4 -0
  89. package/src/components/Table/FdsTableHead/FdsTableHead.stories.ts +151 -0
  90. package/src/components/Table/FdsTableHead/FdsTableHead.vue +54 -0
  91. package/src/components/Table/FdsTableHead/types.ts +5 -0
  92. package/src/components/Tabs/FdsTabs/FdsTabs.stories.ts +247 -0
  93. package/src/components/Tabs/FdsTabs/FdsTabs.vue +27 -0
  94. package/src/components/Tabs/FdsTabs/types.ts +4 -0
  95. package/src/components/Tabs/FdsTabsItem/FdsTabsItem.vue +125 -0
  96. package/src/components/Tabs/FdsTabsItem/types.ts +16 -0
  97. package/src/components/Typography/FdsHeading/FdsHeading.stories.ts +93 -0
  98. package/src/components/Typography/FdsHeading/FdsHeading.vue +51 -0
  99. package/src/components/Typography/FdsHeading/types.ts +5 -0
  100. package/src/components/Typography/FdsListHeading/FdsListHeading.stories.ts +58 -0
  101. package/src/components/Typography/FdsListHeading/FdsListHeading.vue +62 -0
  102. package/src/components/Typography/FdsListHeading/types.ts +8 -0
  103. package/src/components/Typography/FdsSeparator/FdsSeparator.stories.ts +31 -0
  104. package/src/components/Typography/FdsSeparator/FdsSeparator.vue +5 -0
  105. package/src/components/Typography/FdsText/FdsText.stories.ts +66 -0
  106. package/src/components/Typography/FdsText/FdsText.vue +28 -0
  107. package/src/components/Typography/FdsText/types.ts +3 -0
  108. package/src/composables/useBoldQuery.ts +29 -0
  109. package/src/composables/useElementFinalSize.ts +24 -0
  110. package/src/composables/useHasSlots.ts +17 -0
  111. package/src/composables/useIsPid.ts +48 -0
  112. package/src/docs/Start/Start.mdx +12 -0
  113. package/src/docs/Usage.md +117 -0
  114. package/src/fonts.css +28 -0
  115. package/src/global-components.ts +75 -0
  116. package/src/index.ts +180 -0
  117. package/src/main.ts +7 -0
  118. package/src/slot-styles.css +93 -0
  119. package/src/style.css +89 -0
  120. package/src/tokens.css +252 -0
  121. package/dist/index.d.ts +0 -2
@@ -0,0 +1,90 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import type { FdsSpinnerProps } from './types'
4
+
5
+ const props = withDefaults(
6
+ defineProps<{
7
+ size?: string
8
+ color?: FdsSpinnerProps['color']
9
+ label?: string
10
+ labelPosition?: FdsSpinnerProps['labelPosition']
11
+ }>(),
12
+ { size: '48px', color: 'inherit', label: '', labelPosition: 'bottom' },
13
+ )
14
+
15
+ const boxStyle = computed(() => ({ width: props.size, height: props.size }))
16
+
17
+ const colorClass = computed(() => {
18
+ switch (props.color) {
19
+ case 'black':
20
+ return 'text-black'
21
+ case 'white':
22
+ return 'text-white'
23
+ case 'blue':
24
+ return 'text-blue-500'
25
+ case 'inherit':
26
+ default:
27
+ return 'text-inherit'
28
+ }
29
+ })
30
+
31
+ const wrapperClasses = computed(() =>
32
+ props.label
33
+ ? props.labelPosition === 'right'
34
+ ? 'flex items-center gap-2'
35
+ : 'inline-flex flex-col items-center gap-2'
36
+ : 'inline-block',
37
+ )
38
+ </script>
39
+
40
+ <template>
41
+ <div
42
+ :class="wrapperClasses"
43
+ role="status"
44
+ :aria-label="label || 'Laddar'"
45
+ >
46
+ <div
47
+ class="relative"
48
+ :style="boxStyle"
49
+ >
50
+ <svg
51
+ class="animate-spin block"
52
+ xmlns="http://www.w3.org/2000/svg"
53
+ viewBox="0 0 24 24"
54
+ :class="colorClass"
55
+ >
56
+ <path
57
+ fill="none"
58
+ fill-rule="evenodd"
59
+ stroke="currentColor"
60
+ stroke-linecap="round"
61
+ stroke-linejoin="round"
62
+ stroke-width="1.5"
63
+ d="M22.942 14.523a11.42 11.42 0 01-1.087 2.848m-2.04 2.66a12.161 12.161 0 01-2.456 1.81M12.993.747c1.017.09 2.017.32 2.971.682m2.924 1.636c.81.622 1.528 1.355 2.131 2.178M22.6 8.2a10.5 10.5 0 01.614 2.984M14.25 22.972C8.162 24.215 2.218 20.286.975 14.197-.267 8.108 3.662 2.165 9.75.922"
64
+ />
65
+ </svg>
66
+ <svg
67
+ class="animate-spin-reverse absolute top-0 left-0"
68
+ xmlns="http://www.w3.org/2000/svg"
69
+ viewBox="0 0 24 24"
70
+ :class="colorClass"
71
+ >
72
+ <path
73
+ fill="none"
74
+ fill-rule="evenodd"
75
+ stroke="currentColor"
76
+ stroke-linecap="round"
77
+ stroke-linejoin="round"
78
+ stroke-width="1.5"
79
+ d="M12 4.447a7.5 7.5 0 11-7.5 7.5"
80
+ />
81
+ </svg>
82
+ </div>
83
+ <div
84
+ v-if="label"
85
+ :class="labelPosition === 'bottom' ? 'font-bold' : ''"
86
+ >
87
+ {{ label }}
88
+ </div>
89
+ </div>
90
+ </template>
@@ -0,0 +1,6 @@
1
+ export interface FdsSpinnerProps {
2
+ size?: string
3
+ color?: 'black' | 'white' | 'blue' | 'inherit'
4
+ label?: string
5
+ labelPosition?: 'right' | 'bottom'
6
+ }
@@ -0,0 +1,148 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import FdsSticker from './FdsSticker.vue'
3
+
4
+ const meta: Meta<typeof FdsSticker> = {
5
+ title: 'FDS/FdsSticker',
6
+ component: FdsSticker,
7
+ tags: ['autodocs'],
8
+ argTypes: {
9
+ variant: {
10
+ control: { type: 'radio' },
11
+ options: ['blue', 'gray', 'green', 'red', 'yellow', 't_blue'],
12
+ description: 'Set the color variant of the sticker',
13
+ },
14
+ bullet: {
15
+ control: { type: 'boolean' },
16
+ description: 'Set the layout to bullet style',
17
+ },
18
+ default: { control: { type: 'text' } },
19
+ },
20
+ args: {
21
+ variant: 'blue',
22
+ bullet: false,
23
+ default: 'Stick it!',
24
+ },
25
+ }
26
+
27
+ export default meta
28
+ type Story = StoryObj<typeof meta>
29
+
30
+ const stickerTransform = (storyContext: { args?: { variant?: string; bullet?: boolean; default?: string } }) => {
31
+ const args = storyContext?.args || {}
32
+ const attrs = []
33
+
34
+ if (args.variant && args.variant !== 'blue') attrs.push(`variant="${args.variant}"`)
35
+ if (args.bullet) attrs.push(':bullet="true"')
36
+
37
+ const attrsStr = attrs.length ? ` ${attrs.join(' ')}` : ''
38
+ const content = args.default || ''
39
+ return `<FdsSticker${attrsStr}>${content}</FdsSticker>`
40
+ }
41
+
42
+ export const Default: Story = {
43
+ render: (args) => ({
44
+ components: { FdsSticker },
45
+ setup: () => ({ args }),
46
+ template: `<FdsSticker v-bind="args">{{ args.default }}</FdsSticker>`,
47
+ }),
48
+ parameters: {
49
+ docs: {
50
+ source: {
51
+ transform: stickerTransform,
52
+ },
53
+ },
54
+ },
55
+ args: {
56
+ variant: 'blue',
57
+ default: 'Stick it!',
58
+ },
59
+ }
60
+
61
+ export const Alert: Story = {
62
+ render: () => ({
63
+ components: { FdsSticker },
64
+ template: `
65
+ <FdsSticker variant="yellow">Alert</FdsSticker>
66
+ `,
67
+ }),
68
+ }
69
+
70
+ export const Fail: Story = {
71
+ render: () => ({
72
+ components: { FdsSticker },
73
+ template: `
74
+ <FdsSticker variant="red">Failed</FdsSticker>
75
+ `,
76
+ }),
77
+ }
78
+
79
+ export const Success: Story = {
80
+ render: () => ({
81
+ components: { FdsSticker },
82
+ template: `
83
+ <FdsSticker variant="green">Success</FdsSticker>
84
+ `,
85
+ }),
86
+ }
87
+
88
+ export const Gray: Story = {
89
+ render: () => ({
90
+ components: { FdsSticker },
91
+ template: `
92
+ <FdsSticker variant="gray">Info</FdsSticker>
93
+ `,
94
+ }),
95
+ }
96
+
97
+ export const TransparentBlue: Story = {
98
+ render: () => ({
99
+ components: { FdsSticker },
100
+ template: `
101
+ <FdsSticker variant="t_blue">Transparent</FdsSticker>
102
+ `,
103
+ }),
104
+ }
105
+
106
+ export const BulletBlue: Story = {
107
+ render: () => ({
108
+ components: { FdsSticker },
109
+ template: `
110
+ <FdsSticker variant="blue" bullet>Bullet point</FdsSticker>
111
+ `,
112
+ }),
113
+ }
114
+
115
+ export const BulletGreen: Story = {
116
+ render: () => ({
117
+ components: { FdsSticker },
118
+ template: `
119
+ <FdsSticker variant="green" bullet>Success bullet</FdsSticker>
120
+ `,
121
+ }),
122
+ }
123
+
124
+ export const AllVariants: Story = {
125
+ render: () => ({
126
+ components: { FdsSticker },
127
+ template: `
128
+ <div class="space-y-4">
129
+ <div class="space-x-2">
130
+ <FdsSticker variant="blue">Blue</FdsSticker>
131
+ <FdsSticker variant="green">Green</FdsSticker>
132
+ <FdsSticker variant="red">Red</FdsSticker>
133
+ <FdsSticker variant="yellow">Yellow</FdsSticker>
134
+ <FdsSticker variant="gray">Gray</FdsSticker>
135
+ <FdsSticker variant="t_blue">Transparent Blue</FdsSticker>
136
+ </div>
137
+ <div class="space-x-2">
138
+ <FdsSticker variant="blue" bullet>Blue Bullet</FdsSticker>
139
+ <FdsSticker variant="green" bullet>Green Bullet</FdsSticker>
140
+ <FdsSticker variant="red" bullet>Red Bullet</FdsSticker>
141
+ <FdsSticker variant="yellow" bullet>Yellow Bullet</FdsSticker>
142
+ <FdsSticker variant="gray" bullet>Gray Bullet</FdsSticker>
143
+ <FdsSticker variant="t_blue" bullet>Transparent Blue Bullet</FdsSticker>
144
+ </div>
145
+ </div>
146
+ `,
147
+ }),
148
+ }
@@ -0,0 +1,44 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import type { FdsStickerProps } from './types'
4
+
5
+ const props = withDefaults(defineProps<FdsStickerProps>(), {
6
+ variant: 'blue',
7
+ bullet: false,
8
+ })
9
+
10
+ const stickerClasses = computed(() => [
11
+ 'inline-block font-bold text-sm leading-[18px] tracking-normal py-0.5 px-3 rounded-xl border border-transparent whitespace-nowrap max-w-full',
12
+ props.bullet
13
+ ? 'text-base! bg-transparent flex-wrap justify-start items-baseline text-base font-normal text-gray-700 border-transparent p-0 mr-3'
14
+ : '',
15
+ // Variant colors
16
+ props.variant === 'blue' && !props.bullet && 'bg-blue-600 text-white',
17
+ props.variant === 'green' && !props.bullet && 'bg-green-700 text-white',
18
+ props.variant === 'red' && !props.bullet && 'bg-red-600 text-white',
19
+ props.variant === 'yellow' && !props.bullet && 'bg-yellow-200 text-gray-700 border-yellow-300',
20
+ props.variant === 'gray' && !props.bullet && 'bg-gray-200 text-gray-700 border-gray-300!',
21
+ props.variant === 't_blue' && !props.bullet && 'bg-blue_t-100 text-gray-700 border-blue_t-200!',
22
+ ])
23
+
24
+ const bulletDotClasses = computed(() => [
25
+ 'inline-block w-3 h-3 rounded-full mr-1.5',
26
+ // Bullet dot colors
27
+ props.variant === 'blue' && 'bg-blue-600',
28
+ props.variant === 'green' && 'bg-green-700',
29
+ props.variant === 'red' && 'bg-red-600',
30
+ props.variant === 'yellow' && 'bg-yellow-200 border border-yellow-300',
31
+ props.variant === 'gray' && 'bg-gray-400',
32
+ props.variant === 't_blue' && 'bg-blue-200 border border-blue-300',
33
+ ])
34
+ </script>
35
+
36
+ <template>
37
+ <span :class="stickerClasses">
38
+ <span
39
+ v-if="bullet"
40
+ :class="bulletDotClasses"
41
+ ></span>
42
+ <slot />
43
+ </span>
44
+ </template>
@@ -0,0 +1,4 @@
1
+ export interface FdsStickerProps {
2
+ variant?: 'blue' | 'green' | 'red' | 'yellow' | 'gray' | 't_blue'
3
+ bullet?: boolean
4
+ }
@@ -0,0 +1,136 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import FdsTreeView from './FdsTreeView.vue'
3
+ import type { FdsTreeNode as TreeNodeType } from './types'
4
+
5
+ const demoTree: TreeNodeType = {
6
+ nodeId: 'root',
7
+ title: 'Root',
8
+ data: { type: 'root' },
9
+ children: [
10
+ {
11
+ nodeId: 'root-1',
12
+ title: 'Documents',
13
+ children: [
14
+ {
15
+ nodeId: 'doc-1',
16
+ title: 'Work',
17
+ children: [
18
+ {
19
+ nodeId: 'work-1',
20
+ title: 'Projects',
21
+ children: [
22
+ {
23
+ nodeId: 'proj-1',
24
+ title: 'Project Alpha',
25
+ data: { type: 'project', status: 'active' },
26
+ },
27
+ {
28
+ nodeId: 'proj-2',
29
+ title: 'Project Beta',
30
+ data: { type: 'project', status: 'completed' },
31
+ },
32
+ {
33
+ nodeId: 'proj-3',
34
+ title: 'Project Gamma',
35
+ data: { type: 'project', status: 'planning', person_name: 'Anna Sjöström' },
36
+ },
37
+ ],
38
+ },
39
+ {
40
+ nodeId: 'work-2',
41
+ title: 'Reports',
42
+ children: [
43
+ { nodeId: 'rep-1', title: 'Q1 Report', data: { type: 'report', year: 2024 } },
44
+ { nodeId: 'rep-2', title: 'Q2 Report', data: { type: 'report', year: 2024 } },
45
+ ],
46
+ },
47
+ ],
48
+ },
49
+ {
50
+ nodeId: 'doc-2',
51
+ title: 'Personal',
52
+ children: [
53
+ { nodeId: 'pers-1', title: 'Photos', data: { type: 'folder', count: 150 } },
54
+ { nodeId: 'pers-2', title: 'Videos', data: { type: 'folder', count: 25 } },
55
+ { nodeId: 'pers-3', title: 'Music', data: { type: 'folder', count: 500 } },
56
+ ],
57
+ },
58
+ ],
59
+ },
60
+ {
61
+ nodeId: 'root-2',
62
+ title: 'Applications',
63
+ children: [
64
+ {
65
+ nodeId: 'app-1',
66
+ title: 'Development',
67
+ children: [
68
+ { nodeId: 'dev-1', title: 'VS Code', data: { type: 'app', category: 'editor' } },
69
+ { nodeId: 'dev-2', title: 'Terminal', data: { type: 'app', category: 'utility' } },
70
+ { nodeId: 'dev-3', title: 'Git', data: { type: 'app', category: 'version-control' } },
71
+ ],
72
+ },
73
+ {
74
+ nodeId: 'app-2',
75
+ title: 'Design',
76
+ children: [
77
+ { nodeId: 'design-1', title: 'Figma', data: { type: 'app', category: 'design' } },
78
+ { nodeId: 'design-2', title: 'Sketch', data: { type: 'app', category: 'design' } },
79
+ ],
80
+ },
81
+ ],
82
+ },
83
+ {
84
+ nodeId: 'root-3',
85
+ title: 'Settings',
86
+ children: [
87
+ { nodeId: 'set-1', title: 'General', data: { type: 'setting', category: 'preferences' } },
88
+ { nodeId: 'set-2', title: 'Privacy', data: { type: 'setting', category: 'security' } },
89
+ { nodeId: 'set-3', title: 'Notifications', data: { type: 'setting', category: 'alerts' } },
90
+ ],
91
+ },
92
+ ],
93
+ }
94
+
95
+ const meta: Meta<typeof FdsTreeView> = {
96
+ title: 'FDS/FdsTreeView',
97
+ component: FdsTreeView,
98
+ tags: ['autodocs'],
99
+ args: {
100
+ data: demoTree,
101
+ expandAllChildrenOnParentCheck: true,
102
+ expandChildrenOnParentCheck: true,
103
+ horizontalScroll: true,
104
+ indentation: 36,
105
+ nodeCollapseIcon: 'arrowUp',
106
+ nodeExpandIcon: 'arrowDown',
107
+ searchEnabled: true,
108
+ searchExpandNodes: true,
109
+ searchHighlighting: true,
110
+ searchLabel: 'Sök noder',
111
+ searchMatchParams: ['title'],
112
+ showIndeterminate: true,
113
+ showIndeterminateOnlyOnChildrenSelection: true,
114
+ titleTemplate: '[[title]] ([[nodeId]])',
115
+ searchResultNoMatchesTitle: 'Inga resultat hittades',
116
+ searchResultNoMatchesBody: 'Pröva sök på något annat',
117
+ searchCountTemplate: '[[filteredNodes]] av [[totalNodes]] avdelningar',
118
+ searchCountTemplateUnfiltered: '[[totalNodes]] avdelningar',
119
+ popoverLabels: {
120
+ parentAndChildren: 'Denna och alla underliggande',
121
+ parent: 'Endast denna nivå',
122
+ },
123
+ searchInputTriggerLength: 2,
124
+ },
125
+ }
126
+
127
+ export default meta
128
+ type Story = StoryObj<typeof meta>
129
+
130
+ export const Basic: Story = {
131
+ render: (args) => ({
132
+ components: { FdsTreeView },
133
+ setup: () => ({ args }),
134
+ template: '<FdsTreeView v-bind="args" />',
135
+ }),
136
+ }
@@ -0,0 +1,162 @@
1
+ <script setup lang="ts">
2
+ import { computed, provide, ref, watch } from 'vue'
3
+ import useTreeState from './useTreeState'
4
+ import TreeNode from './TreeNode.vue'
5
+ import { getTitleFromProperties } from './utils'
6
+ import type { FdsTreeNode as TreeNodeType, FdsNodeShape, WrapperProps } from './types'
7
+ import { useElementFinalSize } from '@/composables/useElementFinalSize'
8
+ import FdsInput from '@/components/Form/FdsInput/FdsInput.vue'
9
+ import FdsBlockContent from '@/components/Blocks/FdsBlockContent/FdsBlockContent.vue'
10
+ import FdsText from '@/components/Typography/FdsText/FdsText.vue'
11
+
12
+ const props = withDefaults(defineProps<WrapperProps<Record<string, unknown>>>(), {
13
+ indentation: 36,
14
+ showNodeDescription: false,
15
+ showChildrenCount: false,
16
+ expandChildrenOnSelect: false,
17
+ showIndeterminate: false,
18
+ expandChildrenOnParentCheck: false,
19
+ expandAllChildrenOnParentCheck: true,
20
+ showIndeterminateOnlyOnChildrenSelection: false,
21
+ searchEnabled: false,
22
+ searchExpandNodes: false,
23
+ horizontalScroll: false,
24
+ searchMatchParams: () => ['title', 'nodeId'],
25
+ titleTemplate: '[[title]]',
26
+ })
27
+
28
+ const emit = defineEmits<{
29
+ (e: 'update:selectedNodes', value: FdsNodeShape[]): void
30
+ (e: 'getSearchContainerHeight', value: number): void
31
+ }>()
32
+
33
+ const rootNode = props.data
34
+ const nodesForState = rootNode ? [rootNode] : []
35
+
36
+ const treeState = useTreeState({
37
+ expandChildrenOnParentCheck: props.expandChildrenOnParentCheck,
38
+ expandAllChildrenOnParentCheck: props.expandAllChildrenOnParentCheck,
39
+ showIndeterminateOnlyOnChildrenSelection: props.showIndeterminateOnlyOnChildrenSelection,
40
+ searchExpandNodes: props.searchExpandNodes,
41
+ titleTemplate: props.titleTemplate,
42
+ nodes: nodesForState,
43
+ searchInputTriggerLength: props.searchInputTriggerLength,
44
+ })
45
+ provide('treeState', treeState)
46
+
47
+ const pruneNode = (node: TreeNodeType): FdsNodeShape => ({
48
+ nodeId: node.nodeId,
49
+ title: node.title,
50
+ data: node.data,
51
+ children: Array.isArray(node.children) ? node.children.map(pruneNode) : undefined,
52
+ })
53
+
54
+ const selectedNodeObjects = computed<FdsNodeShape[]>(() => treeState.selectedNodeObjects.map(pruneNode))
55
+
56
+ const searchContainerRef = ref<HTMLElement | null>(null)
57
+
58
+ useElementFinalSize(searchContainerRef, (height) => {
59
+ emit('getSearchContainerHeight', height)
60
+ })
61
+
62
+ defineExpose({
63
+ selectedNodeObjects,
64
+ selectedNodes: treeState.selectedNodes,
65
+ getSearchContainerHeight: () => searchContainerRef.value?.getBoundingClientRect().height,
66
+ })
67
+
68
+ watch(
69
+ () => treeState.selectedNodeObjects,
70
+ () => {
71
+ emit('update:selectedNodes', selectedNodeObjects.value)
72
+ },
73
+ { deep: true },
74
+ )
75
+
76
+ // Add 1 to the total node count to include the root node
77
+ const totalNodeCount = computed(() => 1 + (treeState?.totalNodeCount.value ?? 0))
78
+ </script>
79
+
80
+ <template>
81
+ <div>
82
+ <div
83
+ v-if="props.searchEnabled"
84
+ ref="searchContainerRef"
85
+ class="pb-4"
86
+ >
87
+ <FdsInput
88
+ :value="treeState?.searchTerm?.value ?? ''"
89
+ @input="treeState?.setSearchTerm(($event.target as HTMLInputElement).value)"
90
+ class="w-full"
91
+ :label="props.searchLabel"
92
+ clear-button
93
+ @clearInput="treeState?.clearSearch()"
94
+ >
95
+ <input id="tree-view-search-input" />
96
+ </FdsInput>
97
+ <div v-if="props.searchCountTemplate || props.searchCountTemplateUnfiltered">
98
+ <h2
99
+ v-if="
100
+ props.searchCountTemplate &&
101
+ treeState?.searchTerm?.value &&
102
+ String(treeState?.searchTerm?.value || '').trim().length >= (treeState?.triggerLength ?? 1)
103
+ "
104
+ class="text-xl"
105
+ >
106
+ {{
107
+ props.searchCountTemplate
108
+ .replace('[[filteredNodes]]', treeState?.filteredMatchCount.value.toString())
109
+ .replace('[[totalNodes]]', totalNodeCount.toString())
110
+ }}
111
+ </h2>
112
+ <h2
113
+ class="text-xl"
114
+ v-else
115
+ >
116
+ {{
117
+ props.searchCountTemplateUnfiltered
118
+ ?.replace('[[totalNodes]]', totalNodeCount.toString())
119
+ .replace('[[totalNodes]]', totalNodeCount.toString())
120
+ }}
121
+ </h2>
122
+ </div>
123
+ </div>
124
+
125
+ <FdsBlockContent
126
+ v-if="treeState?.isEmptySearchResult?.value"
127
+ class="mt-4"
128
+ >
129
+ <template #header>{{ props.searchResultNoMatchesTitle }}</template>
130
+ <FdsText>{{ props.searchResultNoMatchesBody }}</FdsText>
131
+ </FdsBlockContent>
132
+
133
+ <div :class="['rounded-md', { 'overflow-x-auto bg-white-solid': props.horizontalScroll }]">
134
+ <TreeNode
135
+ v-if="props.data && !treeState?.isEmptySearchResult?.value"
136
+ :style="{ container: 'bg-white-solid px-3 py-2' }"
137
+ :nodes="props.data.children || []"
138
+ :title="getTitleFromProperties(props.data, props.titleTemplate)"
139
+ :nodeId="props.data.nodeId"
140
+ :data="props.data.data"
141
+ :indentation="props.indentation"
142
+ :showNodeDescription="props.showNodeDescription"
143
+ :showChildrenCount="props.showChildrenCount"
144
+ :expandChildrenOnSelect="props.expandChildrenOnSelect"
145
+ :showIndeterminate="props.showIndeterminate"
146
+ :expandChildrenOnParentCheck="props.expandChildrenOnParentCheck"
147
+ :expandAllChildrenOnParentCheck="props.expandAllChildrenOnParentCheck"
148
+ :showIndeterminateOnlyOnChildrenSelection="props.showIndeterminateOnlyOnChildrenSelection"
149
+ :searchEnabled="props.searchEnabled"
150
+ :searchLabel="props.searchLabel"
151
+ :searchExpandNodes="props.searchExpandNodes"
152
+ :searchMatchParams="props.searchMatchParams"
153
+ :horizontalScroll="props.horizontalScroll"
154
+ :nodeExpandIcon="props.nodeExpandIcon"
155
+ :nodeCollapseIcon="props.nodeCollapseIcon"
156
+ :searchHighlighting="props.searchHighlighting"
157
+ :titleTemplate="props.titleTemplate"
158
+ :popoverLabels="props.popoverLabels"
159
+ />
160
+ </div>
161
+ </div>
162
+ </template>