@histoire/controls 0.11.0 → 0.11.1

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 (36) hide show
  1. package/dist/components/HstWrapper.vue.d.ts +16 -3
  2. package/dist/components/button/HstButton.story.vue.d.ts +2 -0
  3. package/dist/components/button/HstButton.vue.d.ts +15 -0
  4. package/dist/components/button/HstButtonGroup.story.vue.d.ts +2 -0
  5. package/dist/components/button/HstButtonGroup.vue.d.ts +24 -0
  6. package/dist/components/checkbox/HstCheckboxList.story.vue.d.ts +2 -0
  7. package/dist/components/checkbox/HstCheckboxList.vue.d.ts +24 -0
  8. package/dist/components/checkbox/HstSimpleCheckbox.story.vue.d.ts +2 -0
  9. package/dist/components/checkbox/HstSimpleCheckbox.vue.d.ts +21 -0
  10. package/dist/index.d.ts +134 -0
  11. package/dist/index.es.js +543 -396
  12. package/dist/style-standalone.css +102 -23
  13. package/package.json +2 -2
  14. package/src/components/HstWrapper.vue +12 -4
  15. package/src/components/button/HstButton.story.vue +30 -0
  16. package/src/components/button/HstButton.vue +26 -0
  17. package/src/components/button/HstButtonGroup.story.vue +51 -0
  18. package/src/components/button/HstButtonGroup.vue +64 -0
  19. package/src/components/checkbox/HstCheckbox.story.vue +5 -1
  20. package/src/components/checkbox/HstCheckbox.vue +4 -50
  21. package/src/components/checkbox/HstCheckboxList.story.vue +49 -0
  22. package/src/components/checkbox/HstCheckboxList.vue +79 -0
  23. package/src/components/checkbox/HstSimpleCheckbox.story.vue +28 -0
  24. package/src/components/checkbox/HstSimpleCheckbox.vue +82 -0
  25. package/src/components/checkbox/__snapshots__/HstCheckbox.test.ts.snap +6 -6
  26. package/src/components/design-tokens/HstColorShades.story.vue +2 -1
  27. package/src/components/design-tokens/HstTokenGrid.story.vue +2 -1
  28. package/src/components/design-tokens/HstTokenList.story.vue +2 -1
  29. package/src/components/number/HstNumber.story.vue +1 -0
  30. package/src/components/number/HstNumber.vue +1 -2
  31. package/src/components/radio/HstRadio.story.vue +5 -1
  32. package/src/components/select/HstSelect.story.vue +1 -0
  33. package/src/components/slider/HstSlider.story.vue +2 -0
  34. package/src/components/text/HstText.story.vue +1 -0
  35. package/src/components/textarea/HstTextarea.story.vue +4 -1
  36. package/src/index.ts +9 -0
@@ -324,6 +324,11 @@
324
324
  margin:0px
325
325
  }
326
326
 
327
+ .-htw-my-1{
328
+ margin-top:-0.25rem;
329
+ margin-bottom:-0.25rem
330
+ }
331
+
327
332
  .htw-my-0{
328
333
  margin-top:0px;
329
334
  margin-bottom:0px
@@ -339,19 +344,14 @@
339
344
  margin-right:1rem
340
345
  }
341
346
 
342
- .-htw-my-1{
343
- margin-top:-0.25rem;
344
- margin-bottom:-0.25rem
347
+ .htw-mr-2{
348
+ margin-right:0.5rem
345
349
  }
346
350
 
347
351
  .htw-mb-2{
348
352
  margin-bottom:0.5rem
349
353
  }
350
354
 
351
- .htw-mr-2{
352
- margin-right:0.5rem
353
- }
354
-
355
355
  .htw-ml-auto{
356
356
  margin-left:auto
357
357
  }
@@ -380,6 +380,10 @@
380
380
  height:1rem
381
381
  }
382
382
 
383
+ .htw-h-\[22px\]{
384
+ height:22px
385
+ }
386
+
383
387
  .htw-h-\[16px\]{
384
388
  height:16px
385
389
  }
@@ -444,14 +448,14 @@
444
448
  width:0.75rem
445
449
  }
446
450
 
447
- .htw-flex-none{
448
- flex:none
449
- }
450
-
451
451
  .htw-flex-1{
452
452
  flex:1 1 0%
453
453
  }
454
454
 
455
+ .htw-flex-none{
456
+ flex:none
457
+ }
458
+
455
459
  .htw-shrink-0{
456
460
  flex-shrink:0
457
461
  }
@@ -474,14 +478,14 @@
474
478
  cursor:pointer
475
479
  }
476
480
 
477
- .htw-cursor-ew-resize{
478
- cursor:ew-resize
479
- }
480
-
481
481
  .htw-cursor-text{
482
482
  cursor:text
483
483
  }
484
484
 
485
+ .htw-cursor-ew-resize{
486
+ cursor:ew-resize
487
+ }
488
+
485
489
  .htw-select-none{
486
490
  -webkit-user-select:none;
487
491
  -moz-user-select:none;
@@ -510,6 +514,10 @@
510
514
  flex-wrap:wrap
511
515
  }
512
516
 
517
+ .htw-flex-nowrap{
518
+ flex-wrap:nowrap
519
+ }
520
+
513
521
  .htw-items-end{
514
522
  align-items:flex-end
515
523
  }
@@ -526,6 +534,10 @@
526
534
  gap:0.25rem
527
535
  }
528
536
 
537
+ .htw-gap-px{
538
+ gap:1px
539
+ }
540
+
529
541
  .htw-gap-4{
530
542
  gap:1rem
531
543
  }
@@ -552,6 +564,10 @@
552
564
  border-radius:0.25rem
553
565
  }
554
566
 
567
+ .\!htw-rounded-\[3px\]{
568
+ border-radius:3px !important
569
+ }
570
+
555
571
  .htw-rounded{
556
572
  border-radius:0.375rem
557
573
  }
@@ -580,20 +596,34 @@
580
596
  border-style:solid
581
597
  }
582
598
 
599
+ .htw-border-black\/25{
600
+ border-color:rgb(0 0 0 / 0.25)
601
+ }
602
+
583
603
  .htw-border-primary-500{
584
604
  --tw-border-opacity:1;
585
605
  border-color:rgb(16 185 129 / var(--tw-border-opacity))
586
606
  }
587
607
 
588
- .htw-border-black\/25{
589
- border-color:rgb(0 0 0 / 0.25)
590
- }
591
-
592
608
  .htw-border-gray-850{
593
609
  --tw-border-opacity:1;
594
610
  border-color:rgb(31 31 33 / var(--tw-border-opacity))
595
611
  }
596
612
 
613
+ .htw-bg-gray-200{
614
+ --tw-bg-opacity:1;
615
+ background-color:rgb(228 228 231 / var(--tw-bg-opacity))
616
+ }
617
+
618
+ .htw-bg-primary-500{
619
+ --tw-bg-opacity:1;
620
+ background-color:rgb(16 185 129 / var(--tw-bg-opacity))
621
+ }
622
+
623
+ .htw-bg-transparent{
624
+ background-color:transparent
625
+ }
626
+
597
627
  .htw-bg-white{
598
628
  --tw-bg-opacity:1;
599
629
  background-color:rgb(255 255 255 / var(--tw-bg-opacity))
@@ -607,10 +637,6 @@
607
637
  background-color:rgb(113 113 122 / 0.5)
608
638
  }
609
639
 
610
- .htw-bg-transparent{
611
- background-color:transparent
612
- }
613
-
614
640
  .htw-bg-gray-50{
615
641
  --tw-bg-opacity:1;
616
642
  background-color:rgb(250 250 250 / var(--tw-bg-opacity))
@@ -656,10 +682,19 @@
656
682
  padding:0.5rem
657
683
  }
658
684
 
685
+ .htw-p-px{
686
+ padding:1px
687
+ }
688
+
659
689
  .htw-p-4{
660
690
  padding:1rem
661
691
  }
662
692
 
693
+ .htw-px-1{
694
+ padding-left:0.25rem;
695
+ padding-right:0.25rem
696
+ }
697
+
663
698
  .htw-py-1{
664
699
  padding-top:0.25rem;
665
700
  padding-bottom:0.25rem
@@ -688,6 +723,11 @@
688
723
  line-height:1.5
689
724
  }
690
725
 
726
+ .htw-text-gray-900{
727
+ --tw-text-opacity:1;
728
+ color:rgb(24 24 27 / var(--tw-text-opacity))
729
+ }
730
+
691
731
  .htw-text-white{
692
732
  --tw-text-opacity:1;
693
733
  color:rgb(255 255 255 / var(--tw-text-opacity))
@@ -776,6 +816,20 @@ body {
776
816
  background-color:rgb(209 250 229 / var(--tw-bg-opacity))
777
817
  }
778
818
 
819
+ .hover\:htw-bg-primary-200:hover{
820
+ --tw-bg-opacity:1;
821
+ background-color:rgb(167 243 208 / var(--tw-bg-opacity))
822
+ }
823
+
824
+ .hover\:htw-bg-primary-600:hover{
825
+ --tw-bg-opacity:1;
826
+ background-color:rgb(5 150 105 / var(--tw-bg-opacity))
827
+ }
828
+
829
+ .hover\:htw-bg-gray-500\/20:hover{
830
+ background-color:rgb(113 113 122 / 0.2)
831
+ }
832
+
779
833
  .hover\:htw-text-primary-500:hover{
780
834
  --tw-text-opacity:1;
781
835
  color:rgb(16 185 129 / var(--tw-text-opacity))
@@ -803,6 +857,11 @@ body {
803
857
  border-color:rgb(255 255 255 / 0.25)
804
858
  }
805
859
 
860
+ .htw-dark .dark\:htw-bg-gray-750{
861
+ --tw-bg-opacity:1;
862
+ background-color:rgb(50 50 56 / var(--tw-bg-opacity))
863
+ }
864
+
806
865
  .htw-dark .dark\:htw-bg-black{
807
866
  --tw-bg-opacity:1;
808
867
  background-color:rgb(0 0 0 / var(--tw-bg-opacity))
@@ -823,6 +882,16 @@ body {
823
882
  background-color:rgb(82 82 91 / var(--tw-bg-opacity))
824
883
  }
825
884
 
885
+ .htw-dark .dark\:htw-text-gray-100{
886
+ --tw-text-opacity:1;
887
+ color:rgb(244 244 245 / var(--tw-text-opacity))
888
+ }
889
+
890
+ .htw-dark .dark\:htw-text-black{
891
+ --tw-text-opacity:1;
892
+ color:rgb(0 0 0 / var(--tw-text-opacity))
893
+ }
894
+
826
895
  .htw-dark .dark\:hover\:htw-border-primary-500:hover{
827
896
  --tw-border-opacity:1;
828
897
  border-color:rgb(16 185 129 / var(--tw-border-opacity))
@@ -833,6 +902,11 @@ body {
833
902
  background-color:rgb(6 95 70 / var(--tw-bg-opacity))
834
903
  }
835
904
 
905
+ .htw-dark .dark\:hover\:htw-bg-primary-900:hover{
906
+ --tw-bg-opacity:1;
907
+ background-color:rgb(6 78 59 / var(--tw-bg-opacity))
908
+ }
909
+
836
910
  .htw-dark .dark\:hover\:htw-bg-primary-700:hover{
837
911
  --tw-bg-opacity:1;
838
912
  background-color:rgb(4 120 87 / var(--tw-bg-opacity))
@@ -842,3 +916,8 @@ body {
842
916
  --tw-border-opacity:1;
843
917
  border-color:rgb(16 185 129 / var(--tw-border-opacity))
844
918
  }
919
+
920
+ .htw-group:hover .htw-dark .group-hover\:dark\:htw-border-primary-500{
921
+ --tw-border-opacity:1;
922
+ border-color:rgb(16 185 129 / var(--tw-border-opacity))
923
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@histoire/controls",
3
- "version": "0.11.0",
3
+ "version": "0.11.1",
4
4
  "description": "Prebuilt controls components",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -31,7 +31,7 @@
31
31
  "*.vue"
32
32
  ],
33
33
  "dependencies": {
34
- "@histoire/vendors": "^0.11.0"
34
+ "@histoire/vendors": "^0.11.1"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@peeky/server": "^0.14.0",
@@ -5,15 +5,23 @@ export default {
5
5
  </script>
6
6
 
7
7
  <script lang="ts" setup>
8
+ import { withDefaults, computed } from 'vue'
8
9
  import { VTooltip as vTooltip } from 'floating-vue'
9
10
 
10
- defineProps<{
11
+ const props = withDefaults(defineProps<{
11
12
  title?: string
12
- }>()
13
+ tag?: string
14
+ }>(), {
15
+ tag: 'label',
16
+ })
17
+
13
18
  </script>
14
19
 
15
20
  <template>
16
- <label class="htw-p-2 hover:htw-bg-primary-100 dark:hover:htw-bg-primary-800 htw-flex htw-gap-2 htw-flex-wrap">
21
+ <component
22
+ :is="tag"
23
+ class="htw-p-2 hover:htw-bg-primary-100 dark:hover:htw-bg-primary-800 htw-flex htw-gap-2 htw-flex-wrap"
24
+ >
17
25
  <span
18
26
  v-tooltip="{
19
27
  content: title,
@@ -30,5 +38,5 @@ defineProps<{
30
38
  </span>
31
39
  <slot name="actions" />
32
40
  </span>
33
- </label>
41
+ </component>
34
42
  </template>
@@ -0,0 +1,30 @@
1
+ <script lang="ts" setup>
2
+ import HstButton from './HstButton.vue'
3
+
4
+ const variants: Array<{name: string, bind?: unknown}> = [
5
+ { name: 'Default' },
6
+ { name: 'Primary', bind: { color: 'primary' } },
7
+ { name: 'Flat', bind: { color: 'flat' } },
8
+ ]
9
+ </script>
10
+
11
+ <template>
12
+ <Story
13
+ title="HstButton"
14
+ group="controls"
15
+ :layout="{ type: 'grid', width: '200px', iframe: false }"
16
+ >
17
+ <Variant
18
+ v-for="(variant, key) of variants"
19
+ :key="key"
20
+ :title="variant.name"
21
+ >
22
+ <HstButton
23
+ v-bind="variant.bind"
24
+ class="htw-p-2"
25
+ >
26
+ Click me!
27
+ </HstButton>
28
+ </Variant>
29
+ </Story>
30
+ </template>
@@ -0,0 +1,26 @@
1
+ <script lang="ts">
2
+ export default {
3
+ name: 'HstButton',
4
+ }
5
+ </script>
6
+
7
+ <script setup lang="ts">
8
+ const colors = {
9
+ default: 'htw-bg-gray-200 dark:htw-bg-gray-750 htw-text-gray-900 dark:htw-text-gray-100 hover:htw-bg-primary-200 dark:hover:htw-bg-primary-900',
10
+ primary: 'htw-bg-primary-500 hover:htw-bg-primary-600 htw-text-white dark:htw-text-black',
11
+ flat: 'htw-bg-transparent hover:htw-bg-gray-500/20 htw-text-gray-900 dark:htw-text-gray-100',
12
+ }
13
+
14
+ defineProps<{
15
+ color?: keyof typeof colors
16
+ }>()
17
+ </script>
18
+
19
+ <template>
20
+ <button
21
+ class="htw-cursor-pointer htw-rounded-sm"
22
+ :class="colors[color ?? 'default']"
23
+ >
24
+ <slot />
25
+ </button>
26
+ </template>
@@ -0,0 +1,51 @@
1
+ <script lang="ts" setup>
2
+ import HstButtonGroup from './HstButtonGroup.vue'
3
+
4
+ const options = {
5
+ slow: 'Slow',
6
+ fast: 'Fast',
7
+ max: 'Max',
8
+ }
9
+
10
+ const flatOptions = Object.keys(options)
11
+
12
+ const objectOptions = Object.keys(options).map(key => ({
13
+ label: options[key],
14
+ value: key,
15
+ }))
16
+
17
+ function initState () {
18
+ return {
19
+ speed: flatOptions[0],
20
+ }
21
+ }
22
+ </script>
23
+
24
+ <template>
25
+ <Story
26
+ title="HstButtonGroup"
27
+ group="controls"
28
+ :layout="{ type: 'single', iframe: false }"
29
+ >
30
+ <Variant
31
+ title="playground"
32
+ :init-state="initState"
33
+ >
34
+ <template #default="{ state }">
35
+ <HstButtonGroup
36
+ v-model="state.speed"
37
+ title="Label"
38
+ :options="objectOptions"
39
+ />
40
+ </template>
41
+
42
+ <template #controls="{ state }">
43
+ <HstButtonGroup
44
+ v-model="state.speed"
45
+ title="Label"
46
+ :options="objectOptions"
47
+ />
48
+ </template>
49
+ </Variant>
50
+ </Story>
51
+ </template>
@@ -0,0 +1,64 @@
1
+ <script lang="ts">
2
+ export default {
3
+ name: 'HstButtonGroup',
4
+ }
5
+ </script>
6
+
7
+ <script setup lang="ts">
8
+ import { computed, ComputedRef } from 'vue'
9
+ import HstWrapper from '../HstWrapper.vue'
10
+ import { HstControlOption } from '../../types'
11
+ import HstButton from './HstButton.vue'
12
+
13
+ const props = defineProps<{
14
+ title?: string
15
+ modelValue: string
16
+ options: HstControlOption[]
17
+ }>()
18
+
19
+ const formattedOptions: ComputedRef<Record<string, string>> = computed(() => {
20
+ if (Array.isArray(props.options)) {
21
+ return Object.fromEntries(props.options.map((value: string | HstControlOption) => {
22
+ if (typeof value === 'string') {
23
+ return [value, value]
24
+ } else {
25
+ return [value.value, value.label]
26
+ }
27
+ }))
28
+ }
29
+ return props.options
30
+ })
31
+
32
+ const emit = defineEmits<{
33
+ (e: 'update:modelValue', value: string): void
34
+ }>()
35
+
36
+ function selectOption (value: string) {
37
+ emit('update:modelValue', value)
38
+ }
39
+ </script>
40
+
41
+ <template>
42
+ <HstWrapper
43
+ tag="div"
44
+ role="group"
45
+ :title="title"
46
+ class="htw-flex-nowrap htw-items-center"
47
+ >
48
+ <div class="htw-flex htw-gap-px htw-border htw-border-solid htw-border-black/25 dark:htw-border-white/25 htw-rounded-sm htw-p-px">
49
+ <HstButton
50
+ v-for="( label, value ) in formattedOptions"
51
+ :key="value"
52
+ class="htw-px-1 htw-h-[22px] htw-flex-1 !htw-rounded-[3px]"
53
+ :color="value === modelValue ? 'primary' : 'flat'"
54
+ :rounded="false"
55
+ @click="selectOption(value)"
56
+ >
57
+ {{ label }}
58
+ </HstButton>
59
+ </div>
60
+ <template #actions>
61
+ <slot name="actions" />
62
+ </template>
63
+ </HstWrapper>
64
+ </template>
@@ -9,7 +9,11 @@ function initState () {
9
9
  </script>
10
10
 
11
11
  <template>
12
- <Story title="HstCheckbox">
12
+ <Story
13
+ title="HstCheckbox"
14
+ group="controls"
15
+ :layout="{ type: 'single', iframe: false }"
16
+ >
13
17
  <Variant
14
18
  title="playground"
15
19
  :init-state="initState"
@@ -5,36 +5,21 @@ export default {
5
5
  </script>
6
6
 
7
7
  <script lang="ts" setup>
8
- import { computed, ref, watch } from 'vue'
9
8
  import HstWrapper from '../HstWrapper.vue'
9
+ import HstSimpleCheckbox from './HstSimpleCheckbox.vue'
10
10
 
11
11
  const props = defineProps<{
12
12
  modelValue: boolean
13
13
  title?: string
14
14
  }>()
15
15
 
16
- const emit = defineEmits({
16
+ const emits = defineEmits({
17
17
  'update:modelValue': (newValue: boolean) => true,
18
18
  })
19
19
 
20
20
  function toggle () {
21
- emit('update:modelValue', !props.modelValue)
22
- animationEnabled.value = true
21
+ emits('update:modelValue', !props.modelValue)
23
22
  }
24
-
25
- // SVG check
26
-
27
- const path = ref<SVGPathElement>()
28
- const dasharray = ref(0)
29
- const progress = computed(() => props.modelValue ? 1 : 0)
30
- const dashoffset = computed(() => (1 - progress.value) * dasharray.value)
31
-
32
- // animationEnabled prevents the animation from triggering on mounted
33
- const animationEnabled = ref(false)
34
-
35
- watch(path, () => {
36
- dasharray.value = path.value.getTotalLength?.() ?? 21.21
37
- })
38
23
  </script>
39
24
 
40
25
  <template>
@@ -47,38 +32,7 @@ watch(path, () => {
47
32
  @keydown.enter.prevent="toggle()"
48
33
  @keydown.space.prevent="toggle()"
49
34
  >
50
- <div class="htw-text-white htw-w-[16px] htw-h-[16px] htw-relative">
51
- <div
52
- class="htw-border htw-border-solid group-active:htw-bg-gray-500/20 htw-rounded-sm htw-box-border htw-absolute htw-inset-0 htw-transition-border htw-duration-150 htw-ease-out"
53
- :class="[
54
- modelValue
55
- ? 'htw-border-primary-500 htw-border-8'
56
- : 'htw-border-black/25 dark:htw-border-white/25 htw-delay-150',
57
- ]"
58
- />
59
- <svg
60
- width="16"
61
- height="16"
62
- viewBox="0 0 24 24"
63
- class="htw-relative htw-z-10"
64
- >
65
- <path
66
- ref="path"
67
- d="m 4 12 l 5 5 l 10 -10"
68
- fill="none"
69
- class="htw-stroke-white htw-stroke-2 htw-duration-200 htw-ease-in-out"
70
- :class="[
71
- animationEnabled ? 'htw-transition-all' : 'htw-transition-none',
72
- {
73
- 'htw-delay-150': modelValue,
74
- },
75
- ]"
76
- :stroke-dasharray="dasharray"
77
- :stroke-dashoffset="dashoffset"
78
- />
79
- </svg>
80
- </div>
81
-
35
+ <HstSimpleCheckbox :model-value="modelValue" />
82
36
  <template #actions>
83
37
  <slot name="actions" />
84
38
  </template>
@@ -0,0 +1,49 @@
1
+ <script lang="ts" setup>
2
+ import HstCheckboxList from './HstCheckboxList.vue'
3
+
4
+ const options = {
5
+ 'crash-bandicoot': 'Crash Bandicoot',
6
+ 'the-last-of-us': 'The Last of Us',
7
+ 'ghost-of-tsushima': 'Ghost of Tsushima',
8
+ }
9
+
10
+ const objectOptions = Object.keys(options).map(key => ({
11
+ label: options[key],
12
+ value: key,
13
+ }))
14
+
15
+ function initState () {
16
+ return {
17
+ characters: [],
18
+ }
19
+ }
20
+ </script>
21
+
22
+ <template>
23
+ <Story
24
+ title="HstCheckboxList"
25
+ group="controls"
26
+ :layout="{ type: 'single', iframe: false }"
27
+ >
28
+ <Variant
29
+ title="playground"
30
+ :init-state="initState"
31
+ >
32
+ <template #default="{ state }">
33
+ <HstCheckboxList
34
+ v-model="state.characters"
35
+ title="Label"
36
+ :options="objectOptions"
37
+ />
38
+ </template>
39
+
40
+ <template #controls="{ state }">
41
+ <HstCheckboxList
42
+ v-model="state.characters"
43
+ title="Label"
44
+ :options="objectOptions"
45
+ />
46
+ </template>
47
+ </Variant>
48
+ </Story>
49
+ </template>
@@ -0,0 +1,79 @@
1
+ <script lang="ts">
2
+ export default {
3
+ name: 'HstCheckboxList',
4
+ }
5
+ </script>
6
+
7
+ <script lang="ts" setup>
8
+ import { computed, ComputedRef } from 'vue'
9
+ import HstWrapper from '../HstWrapper.vue'
10
+ import { HstControlOption } from '../../types'
11
+ import HstSimpleCheckbox from './HstSimpleCheckbox.vue'
12
+
13
+ const props = defineProps<{
14
+ title?: string
15
+ modelValue: Array<string>
16
+ options: HstControlOption[]
17
+ }>()
18
+
19
+ const formattedOptions: ComputedRef<Record<string, string>> = computed(() => {
20
+ if (Array.isArray(props.options)) {
21
+ return Object.fromEntries(props.options.map((value: string | HstControlOption) => {
22
+ if (typeof value === 'string') {
23
+ return [value, value]
24
+ } else {
25
+ return [value.value, value.label]
26
+ }
27
+ }))
28
+ }
29
+ return props.options
30
+ })
31
+
32
+ const emits = defineEmits<{
33
+ (e: 'update:modelValue', value: Array<string>): void
34
+ }>()
35
+
36
+ function toggleOption (value: string) {
37
+ if (props.modelValue.includes(value)) {
38
+ emits('update:modelValue', props.modelValue.filter(element => element !== value))
39
+ } else {
40
+ emits('update:modelValue', [...props.modelValue, value])
41
+ }
42
+ }
43
+ </script>
44
+
45
+ <template>
46
+ <HstWrapper
47
+ role="group"
48
+ :title="title"
49
+ class="htw-cursor-text"
50
+ :class="$attrs.class"
51
+ :style="$attrs.style"
52
+ >
53
+ <div class="-htw-my-1">
54
+ <template
55
+ v-for="( label, value ) in formattedOptions"
56
+ :key="value"
57
+ >
58
+ <label
59
+ tabindex="0"
60
+ :for="`${value}-radio`"
61
+ class="htw-cursor-pointer htw-flex htw-items-center htw-relative htw-py-1 htw-group"
62
+ @keydown.enter.prevent="toggleOption(value)"
63
+ @keydown.space.prevent="toggleOption(value)"
64
+ @click="toggleOption(value)"
65
+ >
66
+ <HstSimpleCheckbox
67
+ :model-value="modelValue.includes(value)"
68
+ class="htw-mr-2"
69
+ />
70
+ {{ label }}
71
+ </label>
72
+ </template>
73
+ </div>
74
+
75
+ <template #actions>
76
+ <slot name="actions" />
77
+ </template>
78
+ </HstWrapper>
79
+ </template>