@nexxtmove/ui 2.6.0 → 2.7.0
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/dist/index.js +49 -126
- package/dist/nuxt.js +0 -1
- package/package.json +38 -36
- package/src/attribute-contract.test.ts +151 -0
- package/src/components/atoms/AnimatedNumber/AnimatedNumber.stories.ts +36 -0
- package/src/components/atoms/AnimatedNumber/AnimatedNumber.test.ts +21 -0
- package/src/components/atoms/Avatar/Avatar.stories.ts +122 -0
- package/src/components/atoms/Avatar/Avatar.test.ts +134 -0
- package/src/components/atoms/Button/Button.stories.ts +290 -0
- package/src/components/atoms/Button/Button.test.ts +406 -0
- package/src/components/atoms/Card/Card.stories.ts +45 -0
- package/src/components/atoms/Card/Card.test.ts +46 -0
- package/src/components/atoms/Checkbox/Checkbox.stories.ts +35 -0
- package/src/components/atoms/Checkbox/Checkbox.test.ts +55 -0
- package/src/components/atoms/Chip/Chip.stories.ts +274 -0
- package/src/components/atoms/Chip/Chip.test.ts +230 -0
- package/src/components/atoms/EnergyLabel/EnergyLabel.stories.ts +189 -0
- package/src/components/atoms/EnergyLabel/EnergyLabel.test.ts +100 -0
- package/src/components/atoms/FloatingPanel/FloatingPanel.stories.ts +677 -0
- package/src/components/atoms/FloatingPanel/FloatingPanel.test.ts +864 -0
- package/src/components/atoms/Icon/Icon.stories.ts +63 -0
- package/src/components/atoms/Icon/Icon.test.ts +89 -0
- package/src/components/atoms/Loader/Loader.stories.ts +36 -0
- package/src/components/atoms/Loader/Loader.test.ts +40 -0
- package/src/components/atoms/PickerItem/PickerItem.stories.ts +172 -0
- package/src/components/atoms/PickerItem/PickerItem.test.ts +142 -0
- package/src/components/atoms/PickerPreview/PickerPreview.stories.ts +183 -0
- package/src/components/atoms/PickerPreview/PickerPreview.test.ts +137 -0
- package/src/components/atoms/PresenceIndicator/PresenceIndicator.stories.ts +21 -0
- package/src/components/atoms/PropertyListing/PropertyListing.stories.ts +38 -0
- package/src/components/atoms/PropertyListing/PropertyListing.test.ts +46 -0
- package/src/components/atoms/SelectableOption/SelectableOption.stories.ts +131 -0
- package/src/components/atoms/SelectableOption/SelectableOption.test.ts +96 -0
- package/src/components/atoms/Skeleton/Skeleton.stories.ts +27 -0
- package/src/components/atoms/Skeleton/Skeleton.test.ts +28 -0
- package/src/components/atoms/Slider/Slider.stories.ts +151 -0
- package/src/components/atoms/Slider/Slider.test.ts +191 -0
- package/src/components/atoms/Squircle/Squircle.stories.ts +114 -0
- package/src/components/atoms/Squircle/Squircle.test.ts +70 -0
- package/src/components/atoms/Switch/Switch.stories.ts +91 -0
- package/src/components/atoms/Switch/Switch.test.ts +183 -0
- package/src/components/atoms/Tooltip/Tooltip.stories.ts +158 -0
- package/src/components/atoms/Tooltip/Tooltip.test.ts +134 -0
- package/src/components/editor/atoms/EditorDimensionField/EditorDimensionField.stories.ts +124 -0
- package/src/components/editor/atoms/EditorDimensionField/EditorDimensionField.test.ts +464 -0
- package/src/components/editor/atoms/EditorSettingsBlock/EditorSettingsBlock.stories.ts +266 -0
- package/src/components/editor/atoms/EditorSettingsBlock/EditorSettingsBlock.test.ts +421 -0
- package/src/components/editor/atoms/EditorSettingsGroup/EditorSettingsGroup.stories.ts +365 -0
- package/src/components/editor/atoms/EditorSettingsGroup/EditorSettingsGroup.test.ts +484 -0
- package/src/components/editor/composables/useRadioGrid.test.ts +261 -0
- package/src/components/editor/molecules/EditorAlignItems/EditorAlignItems.stories.ts +73 -0
- package/src/components/editor/molecules/EditorAlignItems/EditorAlignItems.test.ts +189 -0
- package/src/components/editor/molecules/EditorAlignmentGrid/EditorAlignmentGrid.stories.ts +309 -0
- package/src/components/editor/molecules/EditorAlignmentGrid/EditorAlignmentGrid.test.ts +359 -0
- package/src/components/editor/molecules/EditorAnchor/EditorAnchor.stories.ts +138 -0
- package/src/components/editor/molecules/EditorAnchor/EditorAnchor.test.ts +344 -0
- package/src/components/editor/molecules/EditorBackgroundSize/EditorBackgroundSize.stories.ts +87 -0
- package/src/components/editor/molecules/EditorBackgroundSize/EditorBackgroundSize.test.ts +262 -0
- package/src/components/editor/molecules/EditorBlur/EditorBlur.stories.ts +115 -0
- package/src/components/editor/molecules/EditorBlur/EditorBlur.test.ts +243 -0
- package/src/components/editor/molecules/EditorBorder/EditorBorder.stories.ts +187 -0
- package/src/components/editor/molecules/EditorBorder/EditorBorder.test.ts +456 -0
- package/src/components/editor/molecules/EditorColor/EditorColor.stories.ts +563 -0
- package/src/components/editor/molecules/EditorColor/EditorColor.test.ts +689 -0
- package/src/components/editor/molecules/EditorColor/EditorColorGradient.test.ts +793 -0
- package/src/components/editor/molecules/EditorColor/gradientClass.test.ts +303 -0
- package/src/components/editor/molecules/EditorDashPattern/EditorDashPattern.stories.ts +72 -0
- package/src/components/editor/molecules/EditorDashPattern/EditorDashPattern.test.ts +124 -0
- package/src/components/editor/molecules/EditorFlexDirection/EditorFlexDirection.stories.ts +46 -0
- package/src/components/editor/molecules/EditorFlexDirection/EditorFlexDirection.test.ts +97 -0
- package/src/components/editor/molecules/EditorFontSize/EditorFontSize.stories.ts +248 -0
- package/src/components/editor/molecules/EditorFontSize/EditorFontSize.test.ts +288 -0
- package/src/components/editor/molecules/EditorGap/EditorGap.stories.ts +129 -0
- package/src/components/editor/molecules/EditorGap/EditorGap.test.ts +253 -0
- package/src/components/editor/molecules/EditorHeadingType/EditorHeadingType.stories.ts +68 -0
- package/src/components/editor/molecules/EditorHeadingType/EditorHeadingType.test.ts +150 -0
- package/src/components/editor/molecules/EditorJustifyContent/EditorJustifyContent.stories.ts +71 -0
- package/src/components/editor/molecules/EditorJustifyContent/EditorJustifyContent.test.ts +161 -0
- package/src/components/editor/molecules/EditorLetterSpacing/EditorLetterSpacing.stories.ts +204 -0
- package/src/components/editor/molecules/EditorLetterSpacing/EditorLetterSpacing.test.ts +238 -0
- package/src/components/editor/molecules/EditorLineHeight/EditorLineHeight.stories.ts +257 -0
- package/src/components/editor/molecules/EditorLineHeight/EditorLineHeight.test.ts +286 -0
- package/src/components/editor/molecules/EditorLineStyle/EditorLineStyle.stories.ts +135 -0
- package/src/components/editor/molecules/EditorLineStyle/EditorLineStyle.test.ts +230 -0
- package/src/components/editor/molecules/EditorMaxWidth/EditorMaxWidth.stories.ts +154 -0
- package/src/components/editor/molecules/EditorMaxWidth/EditorMaxWidth.test.ts +112 -0
- package/src/components/editor/molecules/EditorOverrideStatus/EditorOverrideStatus.stories.ts +206 -0
- package/src/components/editor/molecules/EditorOverrideStatus/EditorOverrideStatus.test.ts +332 -0
- package/src/components/editor/molecules/EditorPadding/EditorPadding.stories.ts +374 -0
- package/src/components/editor/molecules/EditorPadding/EditorPadding.test.ts +712 -0
- package/src/components/editor/molecules/EditorRadius/EditorRadius.stories.ts +64 -0
- package/src/components/editor/molecules/EditorRadius/EditorRadius.test.ts +137 -0
- package/src/components/editor/molecules/EditorShadow/EditorShadow.stories.ts +103 -0
- package/src/components/editor/molecules/EditorShadow/EditorShadow.test.ts +168 -0
- package/src/components/editor/molecules/EditorSize/EditorSize.stories.ts +84 -0
- package/src/components/editor/molecules/EditorSize/EditorSize.test.ts +253 -0
- package/src/components/editor/molecules/EditorSizeLimit/EditorSizeLimit.stories.ts +49 -0
- package/src/components/editor/molecules/EditorSizeLimit/EditorSizeLimit.test.ts +43 -0
- package/src/components/editor/molecules/EditorTextAlign/EditorTextAlign.stories.ts +69 -0
- package/src/components/editor/molecules/EditorTextAlign/EditorTextAlign.test.ts +155 -0
- package/src/components/molecules/Announcement/Announcement.stories.ts +224 -0
- package/src/components/molecules/Announcement/Announcement.test.ts +161 -0
- package/src/components/molecules/ContactInfoCard/ContactInfoCard.stories.ts +99 -0
- package/src/components/molecules/ContactInfoCard/ContactInfoCard.test.ts +70 -0
- package/src/components/molecules/CustomerJourneyTile/CustomerJourneyTile.stories.ts +148 -0
- package/src/components/molecules/CustomerJourneyTile/CustomerJourneyTile.test.ts +271 -0
- package/src/components/molecules/DoughnutChart/DoughnutChart.stories.ts +45 -0
- package/src/components/molecules/DoughnutChart/DoughnutChart.test.ts +205 -0
- package/src/components/molecules/InfoBlock/InfoBlock.stories.ts +84 -0
- package/src/components/molecules/InfoBlock/InfoBlock.test.ts +101 -0
- package/src/components/molecules/InputField/InputField.stories.ts +117 -0
- package/src/components/molecules/InputField/InputField.test.ts +138 -0
- package/src/components/molecules/InputSelect/InputSelect.stories.ts +116 -0
- package/src/components/molecules/InputSelect/InputSelect.test.ts +127 -0
- package/src/components/molecules/ModalFooter/ModalFooter.stories.ts +204 -0
- package/src/components/molecules/ModalFooter/ModalFooter.test.ts +276 -0
- package/src/components/molecules/PageCard/PageCard.stories.ts +100 -0
- package/src/components/molecules/PageCard/PageCard.test.ts +91 -0
- package/src/components/molecules/PageContainer/PageContainer.stories.ts +90 -0
- package/src/components/molecules/PageContainer/PageContainer.test.ts +50 -0
- package/src/components/molecules/PageMockup/PageMockup.stories.ts +38 -0
- package/src/components/molecules/PageMockup/PageMockup.test.ts +20 -0
- package/src/components/molecules/PageTypeTile/PageTypeTile.stories.ts +63 -0
- package/src/components/molecules/PageTypeTile/PageTypeTile.test.ts +41 -0
- package/src/components/molecules/Pagination/Pagination.stories.ts +72 -0
- package/src/components/molecules/Pagination/Pagination.test.ts +130 -0
- package/src/components/molecules/PickerGroup/PickerGroup.stories.ts +90 -0
- package/src/components/molecules/PickerGroup/PickerGroup.test.ts +145 -0
- package/src/components/molecules/PickerList/PickerList.stories.ts +436 -0
- package/src/components/molecules/PickerList/PickerList.test.ts +870 -0
- package/src/components/molecules/PlaceholderInput/PlaceholderInput.stories.ts +48 -0
- package/src/components/molecules/PlaceholderInput/PlaceholderInput.test.ts +107 -0
- package/src/components/molecules/PropertyCard/PropertyCard.stories.ts +46 -0
- package/src/components/molecules/PropertyCard/PropertyCard.test.ts +59 -0
- package/src/components/molecules/Select/Select.stories.ts +264 -0
- package/src/components/molecules/Select/Select.test.ts +315 -0
- package/src/components/molecules/SidebarMenuItem/SidebarMenuItem.stories.ts +84 -0
- package/src/components/molecules/SidebarMenuItem/SidebarMenuItem.test.ts +103 -0
- package/src/components/molecules/SkeletonPropertyCard/SkeletonPropertyCard.stories.ts +11 -0
- package/src/components/molecules/SkeletonPropertyCard/SkeletonPropertyCard.test.ts +38 -0
- package/src/components/molecules/SocialIcons/SocialIcons.stories.ts +28 -0
- package/src/components/molecules/SocialIcons/SocialIcons.test.ts +58 -0
- package/src/components/molecules/SocialMediaCustomTemplate/SocialMediaCustomTemplate.stories.ts +11 -0
- package/src/components/molecules/SocialMediaCustomTemplate/SocialMediaCustomTemplate.test.ts +116 -0
- package/src/components/molecules/SocialMediaType/SocialMediaType.stories.ts +37 -0
- package/src/components/molecules/SocialMediaType/SocialMediaType.test.ts +126 -0
- package/src/components/molecules/Source/Source.stories.ts +35 -0
- package/src/components/molecules/Source/Source.test.ts +45 -0
- package/src/components/molecules/TabbedContent/TabbedContent.stories.ts +96 -0
- package/src/components/molecules/TabbedContent/TabbedContent.test.ts +109 -0
- package/src/components/molecules/Tabs/Tabs.stories.ts +230 -0
- package/src/components/molecules/Tabs/Tabs.test.ts +280 -0
- package/src/components/molecules/TemplateCard/TemplateCard.stories.ts +327 -0
- package/src/components/molecules/TemplateCard/TemplateCard.test.ts +339 -0
- package/src/components/molecules/Textarea/Textarea.stories.ts +216 -0
- package/src/components/molecules/Textarea/Textarea.test.ts +191 -0
- package/src/components/molecules/TimelineEvent/TimelineEvent.stories.ts +319 -0
- package/src/components/molecules/TimelineEvent/TimelineEvent.test.ts +224 -0
- package/src/components/molecules/TimelineLanes/TimelineLanes.stories.ts +29 -0
- package/src/components/molecules/TimelineLanes/TimelineLanes.test.ts +30 -0
- package/src/components/molecules/Toast/Toast.stories.ts +110 -0
- package/src/components/molecules/Toast/Toast.test.ts +103 -0
- package/src/components/molecules/ToggleGroup/ToggleGroup.stories.ts +297 -0
- package/src/components/molecules/ToggleGroup/ToggleGroup.test.ts +578 -0
- package/src/components/molecules/Tree/Tree.stories.ts +884 -0
- package/src/components/molecules/Tree/Tree.test.ts +1247 -0
- package/src/components/molecules/Tree/tree-drop.test.ts +223 -0
- package/src/components/organisms/Calendar/Calendar.stories.ts +108 -0
- package/src/components/organisms/Calendar/Calendar.test.ts +279 -0
- package/src/components/organisms/Calendar/_CalendarDayView.test.ts +104 -0
- package/src/components/organisms/Calendar/_CalendarHeader.test.ts +86 -0
- package/src/components/organisms/Calendar/_CalendarMonthView.test.ts +52 -0
- package/src/components/organisms/Calendar/_CalendarTimePicker.test.ts +89 -0
- package/src/components/organisms/Calendar/_CalendarYearView.test.ts +56 -0
- package/src/components/organisms/ColorPicker/ColorPicker.stories.ts +229 -0
- package/src/components/organisms/ColorPicker/ColorPicker.test.ts +638 -0
- package/src/components/organisms/ColorPicker/gradient.test.ts +67 -0
- package/src/components/organisms/ColorPicker/gradientGeometry.test.ts +206 -0
- package/src/components/organisms/Combobox/Combobox.stories.ts +234 -0
- package/src/components/organisms/Combobox/Combobox.test.ts +512 -0
- package/src/components/organisms/Combobox/Combobox.vue +1 -1
- package/src/components/organisms/ComboboxPanel/ComboboxPanel.stories.ts +158 -0
- package/src/components/organisms/ComboboxPanel/ComboboxPanel.test.ts +524 -0
- package/src/components/organisms/ComboboxPanel/ComboboxPanel.vue +1 -1
- package/src/components/organisms/CountrySelect/CountrySelect.stories.ts +139 -0
- package/src/components/organisms/CountrySelect/CountrySelect.test.ts +149 -0
- package/src/components/organisms/DatePicker/DatePicker.stories.ts +163 -0
- package/src/components/organisms/DatePicker/DatePicker.test.ts +214 -0
- package/src/components/organisms/DropdownButton/DropdownButton.stories.ts +123 -0
- package/src/components/organisms/DropdownButton/DropdownButton.test.ts +379 -0
- package/src/components/organisms/FontPicker/FontPicker.stories.ts +476 -0
- package/src/components/organisms/FontPicker/FontPicker.test.ts +873 -0
- package/src/components/organisms/Header/Header.stories.ts +75 -0
- package/src/components/organisms/Header/Header.test.ts +233 -0
- package/src/components/organisms/IconPicker/IconPicker.stories.ts +734 -0
- package/src/components/organisms/IconPicker/IconPicker.test.ts +669 -0
- package/src/components/organisms/IconPicker/_IconPickerGrid.test.ts +184 -0
- package/src/components/organisms/IconPicker/_IconPickerRail.test.ts +102 -0
- package/src/components/organisms/IconPicker/useIconPickerNavigation.test.ts +215 -0
- package/src/components/organisms/Modal/Modal.stories.ts +350 -0
- package/src/components/organisms/Modal/Modal.test.ts +275 -0
- package/src/components/organisms/PickerPanel/PickerPanel.stories.ts +452 -0
- package/src/components/organisms/PickerPanel/PickerPanel.test.ts +358 -0
- package/src/components/organisms/ProgressBar/ProgressBar.stories.ts +100 -0
- package/src/components/organisms/ProgressBar/ProgressBar.test.ts +417 -0
- package/src/components/organisms/ReferenceCard/ReferenceCard.stories.ts +191 -0
- package/src/components/organisms/ReferenceCard/ReferenceCard.test.ts +165 -0
- package/src/components/organisms/SocialMediaTemplate/SocialMediaTemplate.stories.ts +65 -0
- package/src/components/organisms/SocialMediaTemplate/SocialMediaTemplate.test.ts +466 -0
- package/src/components/organisms/StepperHeader/StepperHeader.stories.ts +91 -0
- package/src/components/organisms/StepperHeader/StepperHeader.test.ts +279 -0
- package/src/components/organisms/Table/Table.stories.ts +332 -0
- package/src/components/organisms/Table/Table.test.ts +320 -0
- package/src/components/organisms/TimelinePhaseblock/TimelinePhaseblock.stories.ts +198 -0
- package/src/components/organisms/TimelinePhaseblock/TimelinePhaseblock.test.ts +283 -0
- package/src/components/organisms/TimelineTrack/TimelineTrack.stories.ts +120 -0
- package/src/components/organisms/TimelineTrack/TimelineTrack.test.ts +91 -0
- package/src/components/organisms/Toaster/Toaster.stories.ts +51 -0
- package/src/components/organisms/Toaster/Toaster.test.ts +75 -0
- package/src/components/organisms/VideoControls/VideoControls.stories.ts +792 -0
- package/src/components/organisms/VideoControls/VideoControls.test.ts +1056 -0
- package/src/components/organisms/VideoPlayer/VideoPlayer.stories.ts +327 -0
- package/src/components/organisms/VideoPlayer/VideoPlayer.test.ts +439 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
colorAt,
|
|
4
|
+
gradientCss,
|
|
5
|
+
stopBarCss,
|
|
6
|
+
mixColors,
|
|
7
|
+
normalizeHex,
|
|
8
|
+
wrapAngle,
|
|
9
|
+
clampPercent,
|
|
10
|
+
DEFAULT_GRADIENT,
|
|
11
|
+
} from './gradient'
|
|
12
|
+
|
|
13
|
+
describe('gradient helpers', () => {
|
|
14
|
+
it('wraps angles and clamps percentages to whole numbers', () => {
|
|
15
|
+
expect(wrapAngle(360)).toBe(0)
|
|
16
|
+
expect(wrapAngle(-10)).toBe(350)
|
|
17
|
+
expect(wrapAngle(719.6)).toBe(0)
|
|
18
|
+
expect(clampPercent(-4)).toBe(0)
|
|
19
|
+
expect(clampPercent(100.4)).toBe(100)
|
|
20
|
+
expect(clampPercent(40.5)).toBe(41)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('normalizes hexes to lower case without an opaque ff', () => {
|
|
24
|
+
expect(normalizeHex('#AABBCCFF')).toBe('#aabbcc')
|
|
25
|
+
expect(normalizeHex('#AABBCC80')).toBe('#aabbcc80')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('renders in oklab always, as the app draws it, also off centre', () => {
|
|
29
|
+
expect(gradientCss(DEFAULT_GRADIENT)).toBe(
|
|
30
|
+
'linear-gradient(180deg in oklab, #00000000 0%, #00000099 100%)',
|
|
31
|
+
)
|
|
32
|
+
const radial = { ...DEFAULT_GRADIENT, type: 'radial' as const }
|
|
33
|
+
expect(gradientCss(radial)).toBe('radial-gradient(in oklab, #00000000 0%, #00000099 100%)')
|
|
34
|
+
const offCentre = { ...radial, centerX: 30 }
|
|
35
|
+
expect(gradientCss(offCentre)).toBe(
|
|
36
|
+
'radial-gradient(at 30% 50% in oklab, #00000000 0%, #00000099 100%)',
|
|
37
|
+
)
|
|
38
|
+
expect(stopBarCss(offCentre)).toBe(
|
|
39
|
+
'linear-gradient(to right in oklab, #00000000 0%, #00000099 100%)',
|
|
40
|
+
)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('mixes with premultiplied alpha, so a fade from transparent keeps the hue', () => {
|
|
44
|
+
expect(mixColors('#00000000', '#ff0000', 0.5, 'srgb')).toBe('#ff000080')
|
|
45
|
+
expect(mixColors('#00000000', '#ff0000', 0.5, 'oklab')).toBe('#ff000080')
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('mixes two opaque colours in oklab differently from srgb', () => {
|
|
49
|
+
const oklab = mixColors('#0000ff', '#ffffff', 0.5, 'oklab')
|
|
50
|
+
const srgb = mixColors('#0000ff', '#ffffff', 0.5, 'srgb')
|
|
51
|
+
expect(srgb).toBe('#8080ff')
|
|
52
|
+
expect(oklab).not.toBe(srgb)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('returns the colour at a position, flat beyond the ends', () => {
|
|
56
|
+
const stops = [
|
|
57
|
+
{ color: '#000000', position: 20 },
|
|
58
|
+
{ color: '#ffffff', position: 80 },
|
|
59
|
+
]
|
|
60
|
+
expect(colorAt(stops, 0)).toBe('#000000')
|
|
61
|
+
expect(colorAt(stops, 100)).toBe('#ffffff')
|
|
62
|
+
// halfway in oklab, which is lighter than the srgb #808080
|
|
63
|
+
expect(colorAt(stops, 50)).toBe(mixColors('#000000', '#ffffff', 0.5, 'oklab'))
|
|
64
|
+
expect(colorAt(stops, 50)).not.toBe('#808080')
|
|
65
|
+
expect(colorAt(DEFAULT_GRADIENT.stops, 50)).toBe('#0000004d')
|
|
66
|
+
})
|
|
67
|
+
})
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
angleBetween,
|
|
4
|
+
clampToSquare,
|
|
5
|
+
isInSquare,
|
|
6
|
+
linearHandlePoint,
|
|
7
|
+
farthestCornerAngle,
|
|
8
|
+
linearFromPoints,
|
|
9
|
+
linearLength,
|
|
10
|
+
linearPoint,
|
|
11
|
+
linearPosition,
|
|
12
|
+
projectOnSegment,
|
|
13
|
+
radialEllipse,
|
|
14
|
+
radialPoint,
|
|
15
|
+
radialPosition,
|
|
16
|
+
radialRay,
|
|
17
|
+
radialT,
|
|
18
|
+
radialAngle,
|
|
19
|
+
viaFraction,
|
|
20
|
+
lerpPoint,
|
|
21
|
+
} from './gradientGeometry'
|
|
22
|
+
|
|
23
|
+
const S = 176
|
|
24
|
+
|
|
25
|
+
describe('linear geometry', () => {
|
|
26
|
+
it('has the CSS gradient line length', () => {
|
|
27
|
+
expect(linearLength(0, S)).toBeCloseTo(S)
|
|
28
|
+
expect(linearLength(90, S)).toBeCloseTo(S)
|
|
29
|
+
expect(linearLength(45, S)).toBeCloseTo(S * Math.SQRT2)
|
|
30
|
+
expect(linearLength(135, S)).toBeCloseTo(S * Math.SQRT2)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('puts 0 % and 100 % where CSS does', () => {
|
|
34
|
+
// 0° runs upwards: 0 % at the bottom edge, 100 % at the top edge.
|
|
35
|
+
expect(linearPoint(0, 0, S)).toMatchObject({ x: S / 2, y: S })
|
|
36
|
+
expect(linearPoint(0, 100, S).y).toBeCloseTo(0)
|
|
37
|
+
// 90° runs to the right.
|
|
38
|
+
expect(linearPoint(90, 0, S).x).toBeCloseTo(0)
|
|
39
|
+
expect(linearPoint(90, 100, S).x).toBeCloseTo(S)
|
|
40
|
+
// 45° and 135° end exactly on the corners of the square.
|
|
41
|
+
expect(linearPoint(45, 100, S).x).toBeCloseTo(S)
|
|
42
|
+
expect(linearPoint(45, 100, S).y).toBeCloseTo(0)
|
|
43
|
+
expect(linearPoint(135, 100, S).x).toBeCloseTo(S)
|
|
44
|
+
expect(linearPoint(135, 100, S).y).toBeCloseTo(S)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('sticks out of the square by at most 0.104·S at 22.5°', () => {
|
|
48
|
+
const end = linearPoint(22.5, 100, S)
|
|
49
|
+
expect(-end.y / S).toBeCloseTo(0.104, 2)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it.each([0, 22, 45, 90, 135, 300])('round-trips angle and positions at %i°', (angle) => {
|
|
53
|
+
const positions = { from: 10, via: 37, to: 85 }
|
|
54
|
+
const from = linearPoint(angle, positions.from, S)
|
|
55
|
+
const to = linearPoint(angle, positions.to, S)
|
|
56
|
+
const f = viaFraction(positions.from, positions.via, positions.to)
|
|
57
|
+
expect(
|
|
58
|
+
linearFromPoints({ from, to, viaFraction: f, previousAngle: 999, size: S, minDistance: 8 }),
|
|
59
|
+
).toEqual({ angle, ...positions })
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('projects a point beside the middle line onto the same position', () => {
|
|
63
|
+
const onLine = linearPoint(90, 30, S)
|
|
64
|
+
expect(linearPosition(90, { x: onLine.x, y: onLine.y + 40 }, S)).toBe(30)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('keeps the previous angle for points closer than the minimum distance', () => {
|
|
68
|
+
expect(angleBetween({ x: 10, y: 10 }, { x: 14, y: 14 }, 77, 8)).toBe(77)
|
|
69
|
+
expect(angleBetween({ x: 10, y: 10 }, { x: 10, y: 0 }, 77, 8)).toBe(0)
|
|
70
|
+
expect(angleBetween({ x: 10, y: 10 }, { x: 0, y: 10 }, 77, 8)).toBe(270)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('clamps positions of points beyond the square', () => {
|
|
74
|
+
expect(linearPosition(90, { x: -30, y: 50 }, S)).toBe(0)
|
|
75
|
+
expect(linearPosition(90, { x: S + 30, y: 50 }, S)).toBe(100)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('projects the via point onto the segment, clamped', () => {
|
|
79
|
+
const a = { x: 0, y: 0 }
|
|
80
|
+
const b = { x: 100, y: 0 }
|
|
81
|
+
expect(projectOnSegment({ x: 25, y: 40 }, a, b)).toBeCloseTo(0.25)
|
|
82
|
+
expect(projectOnSegment({ x: -50, y: 0 }, a, b)).toBe(0)
|
|
83
|
+
expect(projectOnSegment({ x: 150, y: 0 }, a, b)).toBe(1)
|
|
84
|
+
expect(lerpPoint(a, b, 0.25)).toEqual({ x: 25, y: 0 })
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
describe('handles inside the square', () => {
|
|
89
|
+
it('clamps a dragged pointer onto the square', () => {
|
|
90
|
+
expect(clampToSquare({ x: -30, y: 200 }, S)).toEqual({ x: 0, y: S })
|
|
91
|
+
expect(clampToSquare({ x: 50, y: 60 }, S)).toEqual({ x: 50, y: 60 })
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('draws 0 % and 100 % at 22° on the edge, along the gradient line', () => {
|
|
95
|
+
const outside = linearPoint(22, 100, S)
|
|
96
|
+
expect(isInSquare(outside, S)).toBe(false)
|
|
97
|
+
const end = linearHandlePoint(22, 100, S)
|
|
98
|
+
const start = linearHandlePoint(22, 0, S)
|
|
99
|
+
expect(isInSquare(end, S)).toBe(true)
|
|
100
|
+
expect(isInSquare(start, S)).toBe(true)
|
|
101
|
+
// on the edge: the top for 100 %, the bottom for 0 %
|
|
102
|
+
expect(end.y).toBeCloseTo(0)
|
|
103
|
+
expect(start.y).toBeCloseTo(S)
|
|
104
|
+
// still on the line through the middle at 22°
|
|
105
|
+
expect(angleBetween(start, end, -1, 1)).toBe(22)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('leaves a canonical point that is inside where it is', () => {
|
|
109
|
+
expect(linearHandlePoint(90, 100, S)).toEqual(linearPoint(90, 100, S))
|
|
110
|
+
expect(linearHandlePoint(22, 50, S)).toEqual(linearPoint(22, 50, S))
|
|
111
|
+
const inner = linearHandlePoint(45, 80, S)
|
|
112
|
+
const canonical = linearPoint(45, 80, S)
|
|
113
|
+
expect(inner.x).toBeCloseTo(canonical.x)
|
|
114
|
+
expect(inner.y).toBeCloseTo(canonical.y)
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
describe('radial geometry', () => {
|
|
119
|
+
it('runs a centred ellipse through the corners', () => {
|
|
120
|
+
const ellipse = radialEllipse(50, 50, S)
|
|
121
|
+
expect(ellipse.rx).toBeCloseTo((S / 2) * Math.SQRT2)
|
|
122
|
+
expect(ellipse.ry).toBeCloseTo((S / 2) * Math.SQRT2)
|
|
123
|
+
expect(radialPosition(ellipse, { x: S, y: S })).toBe(100)
|
|
124
|
+
expect(radialPosition(ellipse, { x: S / 2, y: S / 2 })).toBe(0)
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('keeps the closest-side proportions and reaches the farthest corner off centre', () => {
|
|
128
|
+
const ellipse = radialEllipse(20, 80, S)
|
|
129
|
+
// closest sides: 20 % horizontally and 20 % vertically, so a circle
|
|
130
|
+
expect(ellipse.rx / ellipse.ry).toBeCloseTo(1)
|
|
131
|
+
// farthest corner is top-right
|
|
132
|
+
expect(radialT(ellipse, { x: S, y: 0 })).toBeCloseTo(1)
|
|
133
|
+
const skewed = radialEllipse(30, 50, S)
|
|
134
|
+
expect(skewed.rx / skewed.ry).toBeCloseTo(0.3 / 0.5)
|
|
135
|
+
expect(radialT(skewed, { x: S, y: 0 })).toBeCloseTo(1)
|
|
136
|
+
expect(radialT(skewed, { x: S, y: S })).toBeCloseTo(1)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('stays within 100 % inside the square', () => {
|
|
140
|
+
const ellipse = radialEllipse(20, 80, S)
|
|
141
|
+
for (const corner of [
|
|
142
|
+
{ x: 0, y: 0 },
|
|
143
|
+
{ x: S, y: 0 },
|
|
144
|
+
{ x: 0, y: S },
|
|
145
|
+
{ x: S, y: S },
|
|
146
|
+
]) {
|
|
147
|
+
expect(radialT(ellipse, corner)).toBeLessThanOrEqual(1 + 1e-9)
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('round-trips positions along the ray to the farthest corner', () => {
|
|
152
|
+
const ellipse = radialEllipse(20, 80, S)
|
|
153
|
+
const psi = farthestCornerAngle(ellipse, S)
|
|
154
|
+
const end = radialPoint(ellipse, psi, 100)
|
|
155
|
+
expect(end.x).toBeCloseTo(S)
|
|
156
|
+
expect(end.y).toBeCloseTo(0)
|
|
157
|
+
for (const position of [0, 13, 50, 87, 100]) {
|
|
158
|
+
expect(radialPosition(ellipse, radialPoint(ellipse, psi, position))).toBe(position)
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('clamps the centre to 1–99 so the ellipse never degenerates', () => {
|
|
163
|
+
const ellipse = radialEllipse(0, 100, S)
|
|
164
|
+
expect(Number.isFinite(ellipse.rx)).toBe(true)
|
|
165
|
+
expect(ellipse.rx).toBeGreaterThan(0)
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
describe('radialRay', () => {
|
|
169
|
+
it('falls back to the farthest corner when the dragged ray puts the end outside', () => {
|
|
170
|
+
// Repro: centre 30/70, via dragged to (60, 100) would put to = 100 % at (104, -43).
|
|
171
|
+
const ellipse = radialEllipse(30, 70, S)
|
|
172
|
+
const dragged = radialAngle(ellipse, { x: 60, y: 100 })
|
|
173
|
+
const outside = radialPoint(ellipse, dragged, 100)
|
|
174
|
+
expect(outside.x).toBeCloseTo(104, 0)
|
|
175
|
+
expect(outside.y).toBeCloseTo(-43, 0)
|
|
176
|
+
const ray = radialRay(ellipse, dragged, 100, S)
|
|
177
|
+
expect(ray).toBe(farthestCornerAngle(ellipse, S))
|
|
178
|
+
const end = radialPoint(ellipse, ray, 100)
|
|
179
|
+
expect(end.x).toBeCloseTo(S)
|
|
180
|
+
expect(end.y).toBeCloseTo(0)
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
it('falls back when a kept ray no longer fits after the centre moved', () => {
|
|
184
|
+
const before = radialEllipse(30, 70, S)
|
|
185
|
+
const dragged = radialAngle(before, { x: 20, y: 170 })
|
|
186
|
+
const position = radialPosition(before, { x: 20, y: 170 })
|
|
187
|
+
expect(radialRay(before, dragged, position, S)).toBe(dragged)
|
|
188
|
+
|
|
189
|
+
const after = radialEllipse(80, 80, S)
|
|
190
|
+
expect(radialPoint(after, dragged, position).y).toBeGreaterThan(S)
|
|
191
|
+
const ray = radialRay(after, dragged, position, S)
|
|
192
|
+
const end = radialPoint(after, ray, position)
|
|
193
|
+
expect(end.x).toBeGreaterThanOrEqual(0)
|
|
194
|
+
expect(end.y).toBeGreaterThanOrEqual(0)
|
|
195
|
+
expect(end.x).toBeLessThanOrEqual(S)
|
|
196
|
+
expect(end.y).toBeLessThanOrEqual(S)
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
it('keeps a dragged ray while the farthest stop stays inside', () => {
|
|
200
|
+
const ellipse = radialEllipse(50, 50, S)
|
|
201
|
+
const dragged = radialAngle(ellipse, { x: 30, y: 88 })
|
|
202
|
+
expect(radialRay(ellipse, dragged, 40, S)).toBe(dragged)
|
|
203
|
+
expect(radialRay(ellipse, null, 40, S)).toBe(farthestCornerAngle(ellipse, S))
|
|
204
|
+
})
|
|
205
|
+
})
|
|
206
|
+
})
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { ref } from 'vue'
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
|
3
|
+
import Combobox from './Combobox.vue'
|
|
4
|
+
import type { ComboboxOption } from '../ComboboxPanel/combobox-types'
|
|
5
|
+
|
|
6
|
+
// Flat args type used for Meta/StoryObj — avoids the discriminated union in
|
|
7
|
+
// ComboboxOptions causing TypeScript to erase props to Record<string,any>.
|
|
8
|
+
type ComboboxStoryArgs = {
|
|
9
|
+
variant?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'link'
|
|
10
|
+
icon?: string
|
|
11
|
+
placeholder?: string
|
|
12
|
+
placement?: 'left' | 'right'
|
|
13
|
+
matchWidth?: boolean
|
|
14
|
+
showChips?: boolean
|
|
15
|
+
fullWidth?: boolean
|
|
16
|
+
singleSelect?: boolean
|
|
17
|
+
options: ComboboxOption[]
|
|
18
|
+
allowNew?: boolean
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const CITIES = [
|
|
22
|
+
{ label: 'Almere', value: 'ALM' },
|
|
23
|
+
{ label: 'Amersfoort', value: 'AMF' },
|
|
24
|
+
{ label: 'Amsterdam', value: 'AMS' },
|
|
25
|
+
{ label: 'Apeldoorn', value: 'APD' },
|
|
26
|
+
{ label: 'Arnhem', value: 'ARN' },
|
|
27
|
+
{ label: 'Bolsward', value: 'BOL' },
|
|
28
|
+
{ label: 'Breda', value: 'BRD' },
|
|
29
|
+
{ label: 'Den Haag', value: 'DHG' },
|
|
30
|
+
{ label: 'Dokkum', value: 'DOK' },
|
|
31
|
+
{ label: 'Dordrecht', value: 'DOR' },
|
|
32
|
+
{ label: 'Eindhoven', value: 'EHV' },
|
|
33
|
+
{ label: 'Enschede', value: 'ENS' },
|
|
34
|
+
{ label: 'Franeker', value: 'FRK' },
|
|
35
|
+
{ label: 'Groningen', value: 'GRQ' },
|
|
36
|
+
{ label: 'Haarlem', value: 'HAA' },
|
|
37
|
+
{ label: 'Harlingen', value: 'HRL' },
|
|
38
|
+
{ label: 'Hindeloopen', value: 'HIN' },
|
|
39
|
+
{ label: 'IJlst', value: 'IJL' },
|
|
40
|
+
{ label: 'Leeuwarden', value: 'LWR' },
|
|
41
|
+
{ label: 'Leiden', value: 'LEI' },
|
|
42
|
+
{ label: 'Maastricht', value: 'MST' },
|
|
43
|
+
{ label: 'Nijmegen', value: 'NJM' },
|
|
44
|
+
{ label: 'Rotterdam', value: 'RTM' },
|
|
45
|
+
{ label: 'Sloten', value: 'SLT' },
|
|
46
|
+
{ label: 'Sneek', value: 'SNK' },
|
|
47
|
+
{ label: 'Stavoren', value: 'STV' },
|
|
48
|
+
{ label: "'s-Hertogenbosch", value: 'HTG' },
|
|
49
|
+
{ label: 'Tilburg', value: 'TLB' },
|
|
50
|
+
{ label: 'Utrecht', value: 'UTC' },
|
|
51
|
+
{ label: 'Workum', value: 'WRK' },
|
|
52
|
+
{ label: 'Zaanstad', value: 'ZAA' },
|
|
53
|
+
{ label: 'Zwolle', value: 'ZWO' },
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
export default {
|
|
57
|
+
title: 'Components/Organisms/Combobox',
|
|
58
|
+
// Cast needed: withDefaults + discriminated union props cause Storybook's
|
|
59
|
+
// component type check to fail; ComboboxStoryArgs is used for args instead.
|
|
60
|
+
component: Combobox as unknown as Meta<ComboboxStoryArgs>['component'],
|
|
61
|
+
decorators: [
|
|
62
|
+
() => ({
|
|
63
|
+
template:
|
|
64
|
+
'<div style="min-height: 450px; padding: 16px; display: flex; align-items: start; justify-content: center; position: relative;"><story /></div>',
|
|
65
|
+
}),
|
|
66
|
+
],
|
|
67
|
+
} satisfies Meta<ComboboxStoryArgs>
|
|
68
|
+
|
|
69
|
+
type Story = StoryObj<ComboboxStoryArgs>
|
|
70
|
+
|
|
71
|
+
export const StringOptions: Story = {
|
|
72
|
+
render: (args) => ({
|
|
73
|
+
components: { Combobox },
|
|
74
|
+
setup() {
|
|
75
|
+
const selected = ref<string[]>([])
|
|
76
|
+
const remove = (val: string) => {
|
|
77
|
+
selected.value = selected.value.filter((v) => v !== val)
|
|
78
|
+
}
|
|
79
|
+
return { args, selected, remove }
|
|
80
|
+
},
|
|
81
|
+
template: `
|
|
82
|
+
<Combobox v-bind="args" v-model="selected">Steden</Combobox>
|
|
83
|
+
<div style="position: absolute; top: 0; right: 0; font-size: 12px; color: #6b7280;">
|
|
84
|
+
<div v-for="val in selected" :key="val" style="cursor: pointer;" @click="remove(val)">{{ val }}</div>
|
|
85
|
+
<em v-if="!selected.length">Geen selectie</em>
|
|
86
|
+
</div>
|
|
87
|
+
`,
|
|
88
|
+
}),
|
|
89
|
+
args: {
|
|
90
|
+
placeholder: 'Zoeken...',
|
|
91
|
+
options: CITIES.map((city) => city.label),
|
|
92
|
+
},
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const AllowNew: Story = {
|
|
96
|
+
render: (args) => ({
|
|
97
|
+
components: { Combobox },
|
|
98
|
+
setup() {
|
|
99
|
+
const selected = ref<(string | number)[]>([])
|
|
100
|
+
const remove = (val: string | number) => {
|
|
101
|
+
selected.value = selected.value.filter((v) => v !== val)
|
|
102
|
+
}
|
|
103
|
+
return { args, selected, remove }
|
|
104
|
+
},
|
|
105
|
+
template: `
|
|
106
|
+
<Combobox v-bind="args" v-model="selected">Tags</Combobox>
|
|
107
|
+
<div style="position: absolute; top: 0; right: 0; font-size: 12px; color: #6b7280;">
|
|
108
|
+
<div v-for="val in selected" :key="val" style="cursor: pointer;" @click="remove(val)">{{ val }}</div>
|
|
109
|
+
<em v-if="!selected.length">Geen selectie</em>
|
|
110
|
+
</div>
|
|
111
|
+
`,
|
|
112
|
+
}),
|
|
113
|
+
args: {
|
|
114
|
+
placeholder: 'Zoeken of toevoegen...',
|
|
115
|
+
options: CITIES.map((city) => city.label),
|
|
116
|
+
allowNew: true,
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export const ObjectOptions: Story = {
|
|
121
|
+
render: (args) => ({
|
|
122
|
+
components: { Combobox },
|
|
123
|
+
setup() {
|
|
124
|
+
const selected = ref<(string | number)[]>([])
|
|
125
|
+
const remove = (val: string | number) => {
|
|
126
|
+
selected.value = selected.value.filter((v) => v !== val)
|
|
127
|
+
}
|
|
128
|
+
return { args, selected, remove }
|
|
129
|
+
},
|
|
130
|
+
template: `
|
|
131
|
+
<Combobox v-bind="args" v-model="selected">Steden</Combobox>
|
|
132
|
+
<div style="position: absolute; top: 0; right: 0; font-size: 12px; color: #6b7280;">
|
|
133
|
+
<div v-for="val in selected" :key="val" style="cursor: pointer;" @click="remove(val)">{{ val }}</div>
|
|
134
|
+
<em v-if="!selected.length">Geen selectie</em>
|
|
135
|
+
</div>
|
|
136
|
+
`,
|
|
137
|
+
}),
|
|
138
|
+
args: {
|
|
139
|
+
placeholder: 'Selecteer een stad...',
|
|
140
|
+
options: CITIES,
|
|
141
|
+
},
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export const SingleSelect: Story = {
|
|
145
|
+
render: (args) => ({
|
|
146
|
+
components: { Combobox },
|
|
147
|
+
setup() {
|
|
148
|
+
const selected = ref<(string | number)[]>([])
|
|
149
|
+
return { args, selected }
|
|
150
|
+
},
|
|
151
|
+
template: `
|
|
152
|
+
<Combobox v-bind="args" v-model="selected">{{ selected[0] ?? 'Kies een stad' }}</Combobox>
|
|
153
|
+
<div style="position: absolute; top: 0; right: 0; font-size: 12px; color: #6b7280;">
|
|
154
|
+
<span>{{ selected[0] ?? '—' }}</span>
|
|
155
|
+
</div>
|
|
156
|
+
`,
|
|
157
|
+
}),
|
|
158
|
+
args: {
|
|
159
|
+
placeholder: 'Zoeken...',
|
|
160
|
+
options: CITIES,
|
|
161
|
+
singleSelect: true,
|
|
162
|
+
},
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export const MatchWidth: Story = {
|
|
166
|
+
render: (args) => ({
|
|
167
|
+
components: { Combobox },
|
|
168
|
+
setup() {
|
|
169
|
+
const selected = ref<(string | number)[]>([])
|
|
170
|
+
return { args, selected }
|
|
171
|
+
},
|
|
172
|
+
template: `
|
|
173
|
+
<div style="width: 320px;">
|
|
174
|
+
<Combobox v-bind="args" v-model="selected" style="width: 100%;" full-width match-width>Kies een stad</Combobox>
|
|
175
|
+
</div>
|
|
176
|
+
`,
|
|
177
|
+
}),
|
|
178
|
+
args: {
|
|
179
|
+
placeholder: 'Zoeken...',
|
|
180
|
+
options: CITIES,
|
|
181
|
+
matchWidth: true,
|
|
182
|
+
fullWidth: true,
|
|
183
|
+
},
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export const FullWidth: Story = {
|
|
187
|
+
decorators: [
|
|
188
|
+
() => ({
|
|
189
|
+
template:
|
|
190
|
+
'<div style="min-height: 450px; padding: 16px; display: flex; flex-direction: column; gap: 8px; position: relative;"><story /></div>',
|
|
191
|
+
}),
|
|
192
|
+
],
|
|
193
|
+
render: (args) => ({
|
|
194
|
+
components: { Combobox },
|
|
195
|
+
setup() {
|
|
196
|
+
const selected = ref<(string | number)[]>([])
|
|
197
|
+
return { args, selected }
|
|
198
|
+
},
|
|
199
|
+
template: `
|
|
200
|
+
<p style="font-size: 12px; color: #6b7280; margin: 0;">The combobox below stretches to fill its container width.</p>
|
|
201
|
+
<div style="width: 100%; border: 1px dashed #d1d5db; padding: 12px; border-radius: 8px; box-sizing: border-box;">
|
|
202
|
+
<Combobox v-bind="args" v-model="selected">Kies een stad</Combobox>
|
|
203
|
+
</div>
|
|
204
|
+
`,
|
|
205
|
+
}),
|
|
206
|
+
args: {
|
|
207
|
+
placeholder: 'Zoeken...',
|
|
208
|
+
options: CITIES,
|
|
209
|
+
fullWidth: true,
|
|
210
|
+
matchWidth: true,
|
|
211
|
+
},
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export const HideChips: Story = {
|
|
215
|
+
render: (args) => ({
|
|
216
|
+
components: { Combobox },
|
|
217
|
+
setup() {
|
|
218
|
+
const selected = ref<(string | number)[]>([])
|
|
219
|
+
return { args, selected }
|
|
220
|
+
},
|
|
221
|
+
template: `
|
|
222
|
+
<Combobox v-bind="args" v-model="selected">Steden</Combobox>
|
|
223
|
+
<div style="position: absolute; top: 0; right: 0; font-size: 12px; color: #6b7280;">
|
|
224
|
+
<div v-for="val in selected" :key="val">{{ val }}</div>
|
|
225
|
+
<em v-if="!selected.length">Geen selectie</em>
|
|
226
|
+
</div>
|
|
227
|
+
`,
|
|
228
|
+
}),
|
|
229
|
+
args: {
|
|
230
|
+
placeholder: 'Zoeken...',
|
|
231
|
+
options: CITIES,
|
|
232
|
+
showChips: false,
|
|
233
|
+
},
|
|
234
|
+
}
|