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.
- package/components.d.ts +8 -0
- package/configs/tsconfig.base.json +2 -1
- package/dist/fds-vue-core.cjs.js +35 -15
- package/dist/fds-vue-core.cjs.js.map +1 -1
- package/dist/fds-vue-core.es.js +35 -15
- package/dist/fds-vue-core.es.js.map +1 -1
- package/dist/global-components.d.ts +35 -33
- package/package.json +23 -21
- package/src/.DS_Store +0 -0
- package/src/App.vue +133 -0
- package/src/apply.css +60 -0
- package/src/assets/icons.ts +517 -0
- package/src/components/Blocks/FdsBlockAlert/FdsBlockAlert.stories.ts +94 -0
- package/src/components/Blocks/FdsBlockAlert/FdsBlockAlert.vue +112 -0
- package/src/components/Blocks/FdsBlockAlert/types.ts +12 -0
- package/src/components/Blocks/FdsBlockContent/FdsBlockContent.stories.ts +110 -0
- package/src/components/Blocks/FdsBlockContent/FdsBlockContent.vue +66 -0
- package/src/components/Blocks/FdsBlockContent/types.ts +6 -0
- package/src/components/Blocks/FdsBlockExpander/FdsBlockExpander.stories.ts +123 -0
- package/src/components/Blocks/FdsBlockExpander/FdsBlockExpander.vue +87 -0
- package/src/components/Blocks/FdsBlockExpander/types.ts +8 -0
- package/src/components/Blocks/FdsBlockInfo/FdsBlockInfo.stories.ts +110 -0
- package/src/components/Blocks/FdsBlockInfo/FdsBlockInfo.vue +75 -0
- package/src/components/Blocks/FdsBlockInfo/types.ts +9 -0
- package/src/components/Blocks/FdsBlockLink/FdsBlockLink.css +9 -0
- package/src/components/Blocks/FdsBlockLink/FdsBlockLink.stories.ts +179 -0
- package/src/components/Blocks/FdsBlockLink/FdsBlockLink.vue +149 -0
- package/src/components/Blocks/FdsBlockLink/types.ts +14 -0
- package/src/components/Buttons/ButtonBaseProps.ts +18 -0
- package/src/components/Buttons/FdsButtonCopy/FdsButtonCopy.stories.ts +53 -0
- package/src/components/Buttons/FdsButtonCopy/FdsButtonCopy.vue +87 -0
- package/src/components/Buttons/FdsButtonCopy/types.ts +8 -0
- package/src/components/Buttons/FdsButtonDownload/FdsButtonDownload.stories.ts +111 -0
- package/src/components/Buttons/FdsButtonDownload/FdsButtonDownload.vue +187 -0
- package/src/components/Buttons/FdsButtonIcon/FdsButtonIcon.stories.ts +55 -0
- package/src/components/Buttons/FdsButtonIcon/FdsButtonIcon.vue +57 -0
- package/src/components/Buttons/FdsButtonIcon/types.ts +12 -0
- package/src/components/Buttons/FdsButtonMinor/FdsButtonMinor.stories.ts +68 -0
- package/src/components/Buttons/FdsButtonMinor/FdsButtonMinor.vue +126 -0
- package/src/components/Buttons/FdsButtonPrimary/FdsButtonPrimary.stories.ts +86 -0
- package/src/components/Buttons/FdsButtonPrimary/FdsButtonPrimary.vue +107 -0
- package/src/components/Buttons/FdsButtonSecondary/FdsButtonSecondary.stories.ts +68 -0
- package/src/components/Buttons/FdsButtonSecondary/FdsButtonSecondary.vue +107 -0
- package/src/components/FdsIcon/FdsIcon.stories.ts +69 -0
- package/src/components/FdsIcon/FdsIcon.vue +34 -0
- package/src/components/FdsIcon/types.ts +9 -0
- package/src/components/FdsModal/FdsModal.stories.ts +241 -0
- package/src/components/FdsModal/FdsModal.vue +269 -0
- package/src/components/FdsModal/types.ts +12 -0
- package/src/components/FdsPagination/FdsPagination.stories.ts +109 -0
- package/src/components/FdsPagination/FdsPagination.vue +193 -0
- package/src/components/FdsPagination/types.ts +6 -0
- package/src/components/FdsSearchSelect/FdsSearchSelect.stories.ts +428 -0
- package/src/components/FdsSearchSelect/FdsSearchSelect.vue +621 -0
- package/src/components/FdsSearchSelect/types.ts +25 -0
- package/src/components/FdsSpinner/FdsSpinner.stories.ts +31 -0
- package/src/components/FdsSpinner/FdsSpinner.vue +90 -0
- package/src/components/FdsSpinner/types.ts +6 -0
- package/src/components/FdsSticker/FdsSticker.stories.ts +148 -0
- package/src/components/FdsSticker/FdsSticker.vue +44 -0
- package/src/components/FdsSticker/types.ts +4 -0
- package/src/components/FdsTreeView/FdsTreeView.stories.ts +136 -0
- package/src/components/FdsTreeView/FdsTreeView.vue +162 -0
- package/src/components/FdsTreeView/TreeNode.vue +383 -0
- package/src/components/FdsTreeView/types.ts +141 -0
- package/src/components/FdsTreeView/useTreeState.ts +607 -0
- package/src/components/FdsTreeView/utils.ts +69 -0
- package/src/components/FdsTruncatedText/FdsTruncatedText.stories.ts +78 -0
- package/src/components/FdsTruncatedText/FdsTruncatedText.vue +85 -0
- package/src/components/FdsTruncatedText/types.ts +6 -0
- package/src/components/Form/FdsCheckbox/FdsCheckbox.stories.ts +275 -0
- package/src/components/Form/FdsCheckbox/FdsCheckbox.vue +155 -0
- package/src/components/Form/FdsCheckbox/types.ts +10 -0
- package/src/components/Form/FdsInput/FdsInput.stories.ts +319 -0
- package/src/components/Form/FdsInput/FdsInput.vue +233 -0
- package/src/components/Form/FdsInput/types.ts +25 -0
- package/src/components/Form/FdsRadio/FdsRadio.stories.ts +63 -0
- package/src/components/Form/FdsRadio/FdsRadio.vue +88 -0
- package/src/components/Form/FdsRadio/types.ts +12 -0
- package/src/components/Form/FdsSelect/FdsSelect.stories.ts +78 -0
- package/src/components/Form/FdsSelect/FdsSelect.vue +136 -0
- package/src/components/Form/FdsSelect/types.ts +13 -0
- package/src/components/Form/FdsTextarea/FdsTextarea.stories.ts +52 -0
- package/src/components/Form/FdsTextarea/FdsTextarea.vue +110 -0
- package/src/components/Form/FdsTextarea/types.ts +12 -0
- package/src/components/Table/FdsTable/FdsTable.stories.ts +221 -0
- package/src/components/Table/FdsTable/FdsTable.vue +25 -0
- package/src/components/Table/FdsTable/types.ts +4 -0
- package/src/components/Table/FdsTableHead/FdsTableHead.stories.ts +151 -0
- package/src/components/Table/FdsTableHead/FdsTableHead.vue +54 -0
- package/src/components/Table/FdsTableHead/types.ts +5 -0
- package/src/components/Tabs/FdsTabs/FdsTabs.stories.ts +247 -0
- package/src/components/Tabs/FdsTabs/FdsTabs.vue +27 -0
- package/src/components/Tabs/FdsTabs/types.ts +4 -0
- package/src/components/Tabs/FdsTabsItem/FdsTabsItem.vue +125 -0
- package/src/components/Tabs/FdsTabsItem/types.ts +16 -0
- package/src/components/Typography/FdsHeading/FdsHeading.stories.ts +93 -0
- package/src/components/Typography/FdsHeading/FdsHeading.vue +51 -0
- package/src/components/Typography/FdsHeading/types.ts +5 -0
- package/src/components/Typography/FdsListHeading/FdsListHeading.stories.ts +58 -0
- package/src/components/Typography/FdsListHeading/FdsListHeading.vue +62 -0
- package/src/components/Typography/FdsListHeading/types.ts +8 -0
- package/src/components/Typography/FdsSeparator/FdsSeparator.stories.ts +31 -0
- package/src/components/Typography/FdsSeparator/FdsSeparator.vue +5 -0
- package/src/components/Typography/FdsText/FdsText.stories.ts +66 -0
- package/src/components/Typography/FdsText/FdsText.vue +28 -0
- package/src/components/Typography/FdsText/types.ts +3 -0
- package/src/composables/useBoldQuery.ts +29 -0
- package/src/composables/useElementFinalSize.ts +24 -0
- package/src/composables/useHasSlots.ts +17 -0
- package/src/composables/useIsPid.ts +48 -0
- package/src/docs/Start/Start.mdx +12 -0
- package/src/docs/Usage.md +117 -0
- package/src/fonts.css +28 -0
- package/src/global-components.ts +75 -0
- package/src/index.ts +180 -0
- package/src/main.ts +7 -0
- package/src/slot-styles.css +93 -0
- package/src/style.css +89 -0
- package/src/tokens.css +252 -0
- package/dist/index.d.ts +0 -2
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3'
|
|
2
|
+
import FdsTabs from '@/components/Tabs/FdsTabs/FdsTabs.vue'
|
|
3
|
+
import FdsTabsItem from '@/components/Tabs/FdsTabsItem/FdsTabsItem.vue'
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof FdsTabs> = {
|
|
6
|
+
title: 'FDS/FdsTabs',
|
|
7
|
+
component: FdsTabs,
|
|
8
|
+
tags: ['autodocs'],
|
|
9
|
+
argTypes: {
|
|
10
|
+
variant: { control: { type: 'select' }, options: ['primary', 'secondary'] },
|
|
11
|
+
block: { control: { type: 'boolean' } },
|
|
12
|
+
},
|
|
13
|
+
args: {
|
|
14
|
+
variant: 'primary',
|
|
15
|
+
block: false,
|
|
16
|
+
},
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default meta
|
|
20
|
+
type Story = StoryObj<typeof meta>
|
|
21
|
+
|
|
22
|
+
const tabsTransform = (storyContext: { args?: { block?: boolean; variant?: string } }) => {
|
|
23
|
+
const args = storyContext?.args || {}
|
|
24
|
+
const attrs = []
|
|
25
|
+
if (args.block) attrs.push(':block="true"')
|
|
26
|
+
if (args.variant && args.variant !== 'primary') attrs.push(`variant="${args.variant}"`)
|
|
27
|
+
const attrsStr = attrs.length ? ` ${attrs.join(' ')}` : ''
|
|
28
|
+
return `<FdsTabs${attrsStr}>
|
|
29
|
+
<FdsTabsItem label="Tab 1" />
|
|
30
|
+
<FdsTabsItem label="Tab 2" active />
|
|
31
|
+
<FdsTabsItem label="Tab 3" />
|
|
32
|
+
</FdsTabs>`
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const Default: Story = {
|
|
36
|
+
render: (args) => ({
|
|
37
|
+
components: { FdsTabs, FdsTabsItem },
|
|
38
|
+
setup: () => ({ args }),
|
|
39
|
+
template: `
|
|
40
|
+
<FdsTabs v-bind="args">
|
|
41
|
+
<FdsTabsItem label="Tab 1" />
|
|
42
|
+
<FdsTabsItem label="Tab 2" active />
|
|
43
|
+
<FdsTabsItem label="Tab 3" />
|
|
44
|
+
</FdsTabs>
|
|
45
|
+
`,
|
|
46
|
+
}),
|
|
47
|
+
parameters: {
|
|
48
|
+
docs: {
|
|
49
|
+
source: {
|
|
50
|
+
transform: tabsTransform,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const Secondary: Story = {
|
|
57
|
+
render: (args) => ({
|
|
58
|
+
components: { FdsTabs, FdsTabsItem },
|
|
59
|
+
setup: () => ({ args }),
|
|
60
|
+
template: `
|
|
61
|
+
<FdsTabs v-bind="args">
|
|
62
|
+
<FdsTabsItem label="Tab 1" />
|
|
63
|
+
<FdsTabsItem label="Tab 2" active />
|
|
64
|
+
<FdsTabsItem label="Tab 3" />
|
|
65
|
+
</FdsTabs>
|
|
66
|
+
`,
|
|
67
|
+
}),
|
|
68
|
+
args: {
|
|
69
|
+
variant: 'secondary',
|
|
70
|
+
},
|
|
71
|
+
parameters: {
|
|
72
|
+
docs: {
|
|
73
|
+
source: {
|
|
74
|
+
code: `
|
|
75
|
+
<FdsTabs variant="secondary">
|
|
76
|
+
<FdsTabsItem label="Tab 1" />
|
|
77
|
+
<FdsTabsItem label="Tab 2" active />
|
|
78
|
+
<FdsTabsItem label="Tab 3" />
|
|
79
|
+
</FdsTabs>
|
|
80
|
+
`,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const Block: Story = {
|
|
87
|
+
render: (args) => ({
|
|
88
|
+
components: { FdsTabs, FdsTabsItem },
|
|
89
|
+
setup: () => ({ args }),
|
|
90
|
+
template: `
|
|
91
|
+
<FdsTabs v-bind="args">
|
|
92
|
+
<FdsTabsItem label="Tab 1" />
|
|
93
|
+
<FdsTabsItem label="Tab 2" active />
|
|
94
|
+
<FdsTabsItem label="Tab 3" />
|
|
95
|
+
</FdsTabs>
|
|
96
|
+
`,
|
|
97
|
+
}),
|
|
98
|
+
args: {
|
|
99
|
+
block: true,
|
|
100
|
+
},
|
|
101
|
+
parameters: {
|
|
102
|
+
docs: {
|
|
103
|
+
source: {
|
|
104
|
+
code: `
|
|
105
|
+
<FdsTabs :block="true">
|
|
106
|
+
<FdsTabsItem label="Tab 1" />
|
|
107
|
+
<FdsTabsItem label="Tab 2" active />
|
|
108
|
+
<FdsTabsItem label="Tab 3" />
|
|
109
|
+
</FdsTabs>
|
|
110
|
+
`,
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export const WithDisabled: Story = {
|
|
117
|
+
render: (args) => ({
|
|
118
|
+
components: { FdsTabs, FdsTabsItem },
|
|
119
|
+
setup: () => ({ args }),
|
|
120
|
+
template: `
|
|
121
|
+
<FdsTabs v-bind="args">
|
|
122
|
+
<FdsTabsItem label="Tab 1" />
|
|
123
|
+
<FdsTabsItem label="Tab 2" active />
|
|
124
|
+
<FdsTabsItem label="Tab 3" disabled />
|
|
125
|
+
</FdsTabs>
|
|
126
|
+
`,
|
|
127
|
+
}),
|
|
128
|
+
parameters: {
|
|
129
|
+
slots: {
|
|
130
|
+
default: {
|
|
131
|
+
template: `
|
|
132
|
+
<FdsTabsItem label="Tab 1" />
|
|
133
|
+
<FdsTabsItem label="Tab 2" active />
|
|
134
|
+
<FdsTabsItem label="Tab 3" disabled />
|
|
135
|
+
`,
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export const WithRouterLinks: Story = {
|
|
142
|
+
render: (args) => ({
|
|
143
|
+
components: { FdsTabs, FdsTabsItem },
|
|
144
|
+
setup: () => ({ args }),
|
|
145
|
+
template: `
|
|
146
|
+
<FdsTabs v-bind="args">
|
|
147
|
+
<FdsTabsItem label="Home" to="/" />
|
|
148
|
+
<FdsTabsItem label="About" to="/about" />
|
|
149
|
+
<FdsTabsItem label="Contact" to="/contact" />
|
|
150
|
+
</FdsTabs>
|
|
151
|
+
`,
|
|
152
|
+
}),
|
|
153
|
+
parameters: {
|
|
154
|
+
docs: {
|
|
155
|
+
source: {
|
|
156
|
+
code: `
|
|
157
|
+
<FdsTabs>
|
|
158
|
+
<FdsTabsItem label="Home" to="/" />
|
|
159
|
+
<FdsTabsItem label="About" to="/about" />
|
|
160
|
+
<FdsTabsItem label="Contact" to="/contact" />
|
|
161
|
+
</FdsTabs>
|
|
162
|
+
`,
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export const WithExactMatch: Story = {
|
|
169
|
+
render: (args) => ({
|
|
170
|
+
components: { FdsTabs, FdsTabsItem },
|
|
171
|
+
setup: () => ({ args }),
|
|
172
|
+
template: `
|
|
173
|
+
<FdsTabs v-bind="args">
|
|
174
|
+
<FdsTabsItem label="Home" to="/" exact />
|
|
175
|
+
<FdsTabsItem label="About" to="/about" exact />
|
|
176
|
+
<FdsTabsItem label="Contact" to="/contact" exact />
|
|
177
|
+
</FdsTabs>
|
|
178
|
+
`,
|
|
179
|
+
}),
|
|
180
|
+
parameters: {
|
|
181
|
+
docs: {
|
|
182
|
+
source: {
|
|
183
|
+
code: `
|
|
184
|
+
<FdsTabs>
|
|
185
|
+
<FdsTabsItem label="Home" to="/" exact />
|
|
186
|
+
<FdsTabsItem label="About" to="/about" exact />
|
|
187
|
+
<FdsTabsItem label="Contact" to="/contact" exact />
|
|
188
|
+
</FdsTabs>
|
|
189
|
+
`,
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export const WithAnchorLinks: Story = {
|
|
196
|
+
render: (args) => ({
|
|
197
|
+
components: { FdsTabs, FdsTabsItem },
|
|
198
|
+
setup: () => ({ args }),
|
|
199
|
+
template: `
|
|
200
|
+
<FdsTabs v-bind="args">
|
|
201
|
+
<FdsTabsItem label="Home" href="/" />
|
|
202
|
+
<FdsTabsItem label="About" href="/about" />
|
|
203
|
+
<FdsTabsItem label="Contact" href="/contact" />
|
|
204
|
+
</FdsTabs>
|
|
205
|
+
`,
|
|
206
|
+
}),
|
|
207
|
+
parameters: {
|
|
208
|
+
docs: {
|
|
209
|
+
source: {
|
|
210
|
+
code: `
|
|
211
|
+
<FdsTabs>
|
|
212
|
+
<FdsTabsItem label="Home" href="/" />
|
|
213
|
+
<FdsTabsItem label="About" href="/about" />
|
|
214
|
+
<FdsTabsItem label="Contact" href="/contact" />
|
|
215
|
+
</FdsTabs>
|
|
216
|
+
`,
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export const WithButtons: Story = {
|
|
223
|
+
render: (args) => ({
|
|
224
|
+
components: { FdsTabs, FdsTabsItem },
|
|
225
|
+
setup: () => ({ args }),
|
|
226
|
+
template: `
|
|
227
|
+
<FdsTabs v-bind="args">
|
|
228
|
+
<FdsTabsItem label="Tab 1" as="button" />
|
|
229
|
+
<FdsTabsItem label="Tab 2" as="button" active />
|
|
230
|
+
<FdsTabsItem label="Tab 3" as="button" />
|
|
231
|
+
</FdsTabs>
|
|
232
|
+
`,
|
|
233
|
+
}),
|
|
234
|
+
parameters: {
|
|
235
|
+
docs: {
|
|
236
|
+
source: {
|
|
237
|
+
code: `
|
|
238
|
+
<FdsTabs>
|
|
239
|
+
<FdsTabsItem label="Tab 1" as="button" />
|
|
240
|
+
<FdsTabsItem label="Tab 2" as="button" active />
|
|
241
|
+
<FdsTabsItem label="Tab 3" as="button" />
|
|
242
|
+
</FdsTabs>
|
|
243
|
+
`,
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, provide } from 'vue'
|
|
3
|
+
import type { FdsTabsProps } from './types'
|
|
4
|
+
|
|
5
|
+
const props = withDefaults(defineProps<FdsTabsProps>(), {
|
|
6
|
+
block: false,
|
|
7
|
+
variant: 'primary',
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
const tabsClasses = computed(() => [
|
|
11
|
+
'inline-flex overflow-auto max-w-full p-0.5 gap-0.5',
|
|
12
|
+
{
|
|
13
|
+
'flex justify-between w-full': props.block,
|
|
14
|
+
'bg-blue_t-100 rounded-[10px] mb-8': props.variant === 'primary',
|
|
15
|
+
'gap-2 mb-4': props.variant === 'secondary',
|
|
16
|
+
},
|
|
17
|
+
])
|
|
18
|
+
|
|
19
|
+
provide('tabsVariant', props.variant)
|
|
20
|
+
provide('tabsBlock', props.block)
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<template>
|
|
24
|
+
<div :class="tabsClasses">
|
|
25
|
+
<slot />
|
|
26
|
+
</div>
|
|
27
|
+
</template>
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, getCurrentInstance, inject, useAttrs } from 'vue'
|
|
3
|
+
import type { FdsTabsItemProps } from './types'
|
|
4
|
+
|
|
5
|
+
defineOptions({
|
|
6
|
+
inheritAttrs: false,
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
const attrs = useAttrs()
|
|
10
|
+
|
|
11
|
+
// Try to get route from vue-router if available
|
|
12
|
+
const instance = getCurrentInstance()
|
|
13
|
+
const route = computed(() => {
|
|
14
|
+
try {
|
|
15
|
+
// Check if vue-router is available via app context
|
|
16
|
+
const router = instance?.appContext.config.globalProperties.$router
|
|
17
|
+
if (router && router.currentRoute) {
|
|
18
|
+
return router.currentRoute.value
|
|
19
|
+
}
|
|
20
|
+
} catch {
|
|
21
|
+
// vue-router not available
|
|
22
|
+
}
|
|
23
|
+
return null
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
const props = withDefaults(defineProps<FdsTabsItemProps>(), {
|
|
27
|
+
to: undefined,
|
|
28
|
+
href: undefined,
|
|
29
|
+
as: undefined,
|
|
30
|
+
active: undefined,
|
|
31
|
+
exact: false,
|
|
32
|
+
disabled: false,
|
|
33
|
+
label: undefined,
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const variant = inject<'primary' | 'secondary'>('tabsVariant', 'primary')
|
|
37
|
+
const block = inject<boolean>('tabsBlock', false)
|
|
38
|
+
|
|
39
|
+
// Auto-detect active state from router if to/href is provided
|
|
40
|
+
const isActive = computed(() => {
|
|
41
|
+
if (props.active !== undefined) return props.active
|
|
42
|
+
const currentRoute = route.value
|
|
43
|
+
if (!currentRoute) return false
|
|
44
|
+
|
|
45
|
+
const currentPath = currentRoute.path
|
|
46
|
+
|
|
47
|
+
if (props.exact) {
|
|
48
|
+
// Exact matching
|
|
49
|
+
if (props.to && currentPath === props.to) return true
|
|
50
|
+
if (props.href && currentPath === props.href) return true
|
|
51
|
+
} else {
|
|
52
|
+
// Include matching - check if current path includes to/href
|
|
53
|
+
if (props.to && currentPath.includes(props.to)) return true
|
|
54
|
+
if (props.href && currentPath.includes(props.href)) return true
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return false
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
const componentType = computed(() => {
|
|
61
|
+
if (props.as) return props.as
|
|
62
|
+
if (props.href) return 'a'
|
|
63
|
+
if (props.to) return 'router-link'
|
|
64
|
+
return 'router-link' // Default to router-link
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
const linkAttrs = computed(() => {
|
|
68
|
+
if (componentType.value === 'a') return { href: props.href || props.to }
|
|
69
|
+
if (componentType.value === 'router-link') {
|
|
70
|
+
// If no to/href provided, use current path or empty string
|
|
71
|
+
return { to: props.to || props.href || route.value?.path || '#' }
|
|
72
|
+
}
|
|
73
|
+
return {}
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
const componentAttrs = computed(() => ({
|
|
77
|
+
...linkAttrs.value,
|
|
78
|
+
...attrs,
|
|
79
|
+
}))
|
|
80
|
+
|
|
81
|
+
const baseClasses = computed(() => {
|
|
82
|
+
const blockClasses = block ? 'flex flex-1' : ''
|
|
83
|
+
return [
|
|
84
|
+
'text-center',
|
|
85
|
+
blockClasses,
|
|
86
|
+
'flex justify-center w-full cursor-pointer font-bold text-base leading-6 m-0 select-none no-underline whitespace-nowrap',
|
|
87
|
+
]
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
const variantClasses = computed(() => {
|
|
91
|
+
if (variant === 'primary') {
|
|
92
|
+
return [
|
|
93
|
+
'bg-transparent py-2 px-4 rounded-lg border-2 border-transparent text-inherit',
|
|
94
|
+
isActive.value
|
|
95
|
+
? 'border-transparent bg-white shadow-md'
|
|
96
|
+
: 'hover:bg-white/59 hover:outline-none active:border-transparent active:bg-white/80',
|
|
97
|
+
'focus-visible:outline-dashed focus-visible:outline-2 focus-visible:outline-blue-500 focus-visible:outline-offset-[-2px]',
|
|
98
|
+
]
|
|
99
|
+
} else {
|
|
100
|
+
return [
|
|
101
|
+
'text-blue-700 py-1 px-2 rounded-md border-none',
|
|
102
|
+
isActive.value ? 'bg-blue-500 text-white' : 'hover:bg-blue_t-100 active:bg-blue_t-200',
|
|
103
|
+
'focus-visible:outline-dashed focus-visible:outline-2 focus-visible:outline-blue-500',
|
|
104
|
+
'tracking-[0.002em]',
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
const disabledClasses = computed(() => (props.disabled ? 'cursor-not-allowed pointer-events-none opacity-35' : ''))
|
|
110
|
+
|
|
111
|
+
const buttonClasses = computed(() => [...baseClasses.value, ...variantClasses.value, disabledClasses.value])
|
|
112
|
+
</script>
|
|
113
|
+
|
|
114
|
+
<template>
|
|
115
|
+
<component
|
|
116
|
+
:is="componentType"
|
|
117
|
+
v-bind="componentAttrs"
|
|
118
|
+
:class="buttonClasses"
|
|
119
|
+
:disabled="componentType === 'button' ? disabled : undefined"
|
|
120
|
+
:aria-disabled="disabled"
|
|
121
|
+
:aria-current="isActive ? 'page' : undefined"
|
|
122
|
+
>
|
|
123
|
+
<slot>{{ label }}</slot>
|
|
124
|
+
</component>
|
|
125
|
+
</template>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface FdsTabsItemProps {
|
|
2
|
+
/** Link destination (for router-link or anchor) */
|
|
3
|
+
to?: string
|
|
4
|
+
/** Link destination (for anchor tag) */
|
|
5
|
+
href?: string
|
|
6
|
+
/** Component type: 'router-link', 'a', or 'button' */
|
|
7
|
+
as?: 'router-link' | 'a' | 'button'
|
|
8
|
+
/** Manually set active state (if not using router) */
|
|
9
|
+
active?: boolean
|
|
10
|
+
/** Use exact path matching (default: false, matches if path includes to/href) */
|
|
11
|
+
exact?: boolean
|
|
12
|
+
/** Disable the tab */
|
|
13
|
+
disabled?: boolean
|
|
14
|
+
/** Tab label text */
|
|
15
|
+
label?: string
|
|
16
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3'
|
|
2
|
+
import FdsHeading from './FdsHeading.vue'
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof FdsHeading> = {
|
|
5
|
+
title: 'FDS/Typography/FdsHeading',
|
|
6
|
+
component: FdsHeading,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
argTypes: {
|
|
9
|
+
type: { control: { type: 'select' }, options: ['h1', 'h2', 'h3'] },
|
|
10
|
+
size: { control: { type: 'select' }, options: ['xl', 'lg', 'md', 'sm', 'responsive'] },
|
|
11
|
+
text: { control: { type: 'text' } },
|
|
12
|
+
},
|
|
13
|
+
args: {
|
|
14
|
+
type: 'h1',
|
|
15
|
+
size: 'responsive',
|
|
16
|
+
text: 'Heading',
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default meta
|
|
21
|
+
type Story = StoryObj<typeof meta>
|
|
22
|
+
|
|
23
|
+
const headingTransform = (storyContext: { args?: { type?: string; size?: string; text?: string } }) => {
|
|
24
|
+
const args = storyContext?.args || {}
|
|
25
|
+
const attrs = []
|
|
26
|
+
|
|
27
|
+
attrs.push(`type="${args.type || 'h1'}"`)
|
|
28
|
+
if (args.size && args.size !== 'responsive') attrs.push(`size="${args.size}"`)
|
|
29
|
+
attrs.push(`text="${args.text || 'Heading'}"`)
|
|
30
|
+
|
|
31
|
+
const attrsStr = ` ${attrs.join(' ')}`
|
|
32
|
+
return `<FdsHeading${attrsStr} />`
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const Default: Story = {
|
|
36
|
+
parameters: {
|
|
37
|
+
docs: {
|
|
38
|
+
source: {
|
|
39
|
+
transform: headingTransform,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const H1: Story = {
|
|
46
|
+
args: {
|
|
47
|
+
type: 'h1',
|
|
48
|
+
text: 'H1 Heading',
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const H2: Story = {
|
|
53
|
+
args: {
|
|
54
|
+
type: 'h2',
|
|
55
|
+
text: 'H2 Heading',
|
|
56
|
+
},
|
|
57
|
+
parameters: {
|
|
58
|
+
docs: {
|
|
59
|
+
source: {
|
|
60
|
+
transform: headingTransform,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const H3: Story = {
|
|
67
|
+
args: {
|
|
68
|
+
type: 'h3',
|
|
69
|
+
text: 'H3 Heading',
|
|
70
|
+
},
|
|
71
|
+
parameters: {
|
|
72
|
+
docs: {
|
|
73
|
+
source: {
|
|
74
|
+
transform: headingTransform,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const Responsive: Story = {
|
|
81
|
+
args: {
|
|
82
|
+
type: 'h1',
|
|
83
|
+
size: 'responsive',
|
|
84
|
+
text: 'Responsive Heading',
|
|
85
|
+
},
|
|
86
|
+
parameters: {
|
|
87
|
+
docs: {
|
|
88
|
+
source: {
|
|
89
|
+
code: `<FdsHeading type="h1" text="Responsive Heading" />`,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
import type { FdsHeadingProps } from './types'
|
|
4
|
+
|
|
5
|
+
const props = withDefaults(defineProps<FdsHeadingProps>(), {
|
|
6
|
+
size: 'responsive',
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
const headingClasses = computed(() => {
|
|
10
|
+
const baseClasses = 'font-raleway leading-tight m-0'
|
|
11
|
+
|
|
12
|
+
if (props.type === 'h1') {
|
|
13
|
+
return [baseClasses, 'mb-3', getResponsiveSize('h1', props.size)].filter(Boolean).join(' ')
|
|
14
|
+
}
|
|
15
|
+
if (props.type === 'h2') {
|
|
16
|
+
return [baseClasses, 'mt-4 mb-2', getResponsiveSize('h2', props.size)].filter(Boolean).join(' ')
|
|
17
|
+
}
|
|
18
|
+
if (props.type === 'h3') {
|
|
19
|
+
return [baseClasses, 'mt-4 mb-2', getResponsiveSize('h3', props.size)].filter(Boolean).join(' ')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return baseClasses
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
function getResponsiveSize(element: 'h1' | 'h2' | 'h3', size: string) {
|
|
26
|
+
if (size !== 'responsive') {
|
|
27
|
+
const sizeMap: Record<typeof element, Record<string, string>> = {
|
|
28
|
+
h1: { xl: 'text-6xl', lg: 'text-5xl', md: 'text-4xl', sm: 'text-2xl' },
|
|
29
|
+
h2: { xl: 'text-5xl', lg: 'text-4xl', md: 'text-3xl', sm: 'text-xl' },
|
|
30
|
+
h3: { xl: 'text-3xl', lg: 'text-2xl', md: 'text-xl', sm: 'text-lg' },
|
|
31
|
+
}
|
|
32
|
+
return sizeMap[element][size]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const responsiveMap: Record<typeof element, string> = {
|
|
36
|
+
h1: 'text-2xl sm:text-4xl md:text-5xl lg:text-6xl',
|
|
37
|
+
h2: 'text-xl sm:text-3xl md:text-4xl lg:text-5xl',
|
|
38
|
+
h3: 'text-lg sm:text-xl md:text-2xl lg:text-3xl',
|
|
39
|
+
}
|
|
40
|
+
return responsiveMap[element]
|
|
41
|
+
}
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<template>
|
|
45
|
+
<component
|
|
46
|
+
:is="type"
|
|
47
|
+
:class="headingClasses"
|
|
48
|
+
>
|
|
49
|
+
{{ text }}
|
|
50
|
+
</component>
|
|
51
|
+
</template>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3'
|
|
2
|
+
import FdsListHeading from './FdsListHeading.vue'
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof FdsListHeading> = {
|
|
5
|
+
title: 'FDS/Typography/FdsListHeading',
|
|
6
|
+
component: FdsListHeading,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
argTypes: {
|
|
9
|
+
loading: { control: { type: 'boolean' } },
|
|
10
|
+
loadingText: { control: { type: 'text' } },
|
|
11
|
+
total: { control: { type: 'number' } },
|
|
12
|
+
unfiltered: { control: { type: 'number' } },
|
|
13
|
+
text: { control: { type: 'text' } },
|
|
14
|
+
language: { control: { type: 'select' }, options: ['sv', 'en'] },
|
|
15
|
+
},
|
|
16
|
+
args: {
|
|
17
|
+
loading: false,
|
|
18
|
+
text: 'resultat',
|
|
19
|
+
total: 10,
|
|
20
|
+
unfiltered: 100,
|
|
21
|
+
language: 'sv',
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default meta
|
|
26
|
+
type Story = StoryObj<typeof meta>
|
|
27
|
+
|
|
28
|
+
export const Default: Story = {}
|
|
29
|
+
|
|
30
|
+
export const Loading: Story = {
|
|
31
|
+
args: {
|
|
32
|
+
loading: true,
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const Filtered: Story = {
|
|
37
|
+
args: {
|
|
38
|
+
total: 25,
|
|
39
|
+
unfiltered: 100,
|
|
40
|
+
text: 'resultat',
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const Unfiltered: Story = {
|
|
45
|
+
args: {
|
|
46
|
+
total: 100,
|
|
47
|
+
text: 'resultat',
|
|
48
|
+
},
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const English: Story = {
|
|
52
|
+
args: {
|
|
53
|
+
total: 25,
|
|
54
|
+
unfiltered: 100,
|
|
55
|
+
text: 'results',
|
|
56
|
+
language: 'en',
|
|
57
|
+
},
|
|
58
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
import FdsSpinner from '@/components/FdsSpinner/FdsSpinner.vue'
|
|
4
|
+
import type { FdsListHeadingProps } from './types'
|
|
5
|
+
|
|
6
|
+
const props = withDefaults(defineProps<FdsListHeadingProps>(), {
|
|
7
|
+
loading: false,
|
|
8
|
+
language: 'sv',
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
const separatorText = () => (props.language === 'sv' ? 'av' : 'of')
|
|
12
|
+
|
|
13
|
+
const headingText = computed(() => {
|
|
14
|
+
// If total is falsy, show "0 separator unfiltered text"
|
|
15
|
+
if (!props.total) {
|
|
16
|
+
let result = '0'
|
|
17
|
+
if (props.unfiltered) {
|
|
18
|
+
result += ` ${separatorText()} ${props.unfiltered}`
|
|
19
|
+
}
|
|
20
|
+
if (props.text) {
|
|
21
|
+
result += ` ${props.text}`
|
|
22
|
+
}
|
|
23
|
+
return result
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// If no text, just show total
|
|
27
|
+
if (!props.text) return String(props.total)
|
|
28
|
+
|
|
29
|
+
// If unfiltered exists and differs from total, show "total separator unfiltered text"
|
|
30
|
+
if (props.unfiltered && props.total !== props.unfiltered) {
|
|
31
|
+
return `${props.total} ${separatorText()} ${props.unfiltered} ${props.text}`
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Default: show "total text"
|
|
35
|
+
return `${props.total} ${props.text}`
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const handleLoadingText = () => {
|
|
39
|
+
if (props.loadingText && props.loadingText.length > 0) {
|
|
40
|
+
return props.loadingText
|
|
41
|
+
}
|
|
42
|
+
return props.language === 'sv' ? 'Laddar...' : 'Loading...'
|
|
43
|
+
}
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<template>
|
|
47
|
+
<div class="inline-block mb-3 h-7 font-bold text-xl">
|
|
48
|
+
<FdsSpinner
|
|
49
|
+
v-if="loading"
|
|
50
|
+
size="24px"
|
|
51
|
+
color="blue"
|
|
52
|
+
:label="handleLoadingText()"
|
|
53
|
+
label-position="right"
|
|
54
|
+
/>
|
|
55
|
+
<h3
|
|
56
|
+
v-else
|
|
57
|
+
class="heading font-heading text-xl m-0"
|
|
58
|
+
>
|
|
59
|
+
{{ headingText }}
|
|
60
|
+
</h3>
|
|
61
|
+
</div>
|
|
62
|
+
</template>
|