sprintify-ui 0.0.32 → 0.0.34

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 (27) hide show
  1. package/dist/sprintify-ui.es.js +3616 -3402
  2. package/dist/types/src/components/BaseAutocomplete.vue.d.ts +9 -9
  3. package/dist/types/src/components/BaseAutocompleteFetch.vue.d.ts +9 -9
  4. package/dist/types/src/components/BaseBelongsTo.vue.d.ts +10 -10
  5. package/dist/types/src/components/BaseButtonGroup.vue.d.ts +23 -23
  6. package/dist/types/src/components/BaseFilePicker.vue.d.ts +1 -1
  7. package/dist/types/src/components/BaseFileUploader.vue.d.ts +4 -4
  8. package/dist/types/src/components/BaseHasMany.vue.d.ts +0 -1
  9. package/dist/types/src/components/BaseRadioGroup.vue.d.ts +226 -0
  10. package/dist/types/src/components/BaseTagAutocomplete.vue.d.ts +0 -1
  11. package/dist/types/src/components/BaseTagAutocompleteFetch.vue.d.ts +0 -1
  12. package/dist/types/src/components/index.d.ts +3 -1
  13. package/dist/types/src/composables/hasOptions.d.ts +7 -0
  14. package/dist/types/src/types/types.d.ts +0 -2
  15. package/package.json +1 -1
  16. package/src/components/BaseAutocomplete.vue +29 -40
  17. package/src/components/BaseAutocompleteFetch.vue +2 -2
  18. package/src/components/BaseBelongsTo.vue +3 -3
  19. package/src/components/BaseButtonGroup.stories.js +5 -8
  20. package/src/components/BaseButtonGroup.vue +24 -66
  21. package/src/components/BaseRadioGroup.stories.js +88 -0
  22. package/src/components/BaseRadioGroup.vue +84 -0
  23. package/src/components/BaseTagAutocomplete.stories.js +4 -6
  24. package/src/components/BaseTagAutocomplete.vue +35 -53
  25. package/src/components/index.ts +4 -0
  26. package/src/composables/hasOptions.ts +68 -0
  27. package/src/types/types.ts +0 -4
@@ -11,6 +11,7 @@ import BaseBelongsTo from './BaseBelongsTo.vue';
11
11
  import BaseBoolean from './BaseBoolean.vue';
12
12
  import BaseBreadcrumbs from './BaseBreadcrumbs.vue';
13
13
  import BaseButton from './BaseButton.vue';
14
+ import BaseButtonGroup from './BaseButtonGroup.vue';
14
15
  import BaseCard from './BaseCard.vue';
15
16
  import BaseCardRow from './BaseCardRow.vue';
16
17
  import BaseCharacterCounter from './BaseCharacterCounter.vue';
@@ -45,6 +46,7 @@ import BasePagination from './BasePagination.vue';
45
46
  import BasePanel from './BasePanel.vue';
46
47
  import BasePassword from './BasePassword.vue';
47
48
  import BaseProgressCircle from './BaseProgressCircle.vue';
49
+ import BaseRadioGroup from './BaseRadioGroup.vue';
48
50
  import BaseReadMore from './BaseReadMore.vue';
49
51
  import BaseSelect from './BaseSelect.vue';
50
52
  import BaseSideNavigation from './BaseSideNavigation.vue';
@@ -64,4 +66,4 @@ import BaseLayoutStacked from './BaseLayoutStacked.vue';
64
66
  import BaseLayoutStackedConfigurable from './BaseLayoutStackedConfigurable.vue';
65
67
  import BaseLayoutSidebar from './BaseLayoutSidebar.vue';
66
68
  import BaseLayoutSidebarConfigurable from './BaseLayoutSidebarConfigurable.vue';
67
- export { BaseActionItem, BaseAlert, BaseApp, BaseAppDialogs, BaseAppNotifications, BaseAutocomplete, BaseAutocompleteFetch, BaseAvatar, BaseBadge, BaseBelongsTo, BaseBoolean, BaseBreadcrumbs, BaseButton, BaseCard, BaseCardRow, BaseCharacterCounter, BaseClipboard, BaseContainer, BaseCounter, BaseDataIterator, BaseDataTable, BaseDatePicker, BaseDateSelect, BaseDescriptionList, BaseDescriptionListItem, BaseDialog, BaseFilePicker, BaseFileUploader, BaseHasMany, BaseIcon, BaseInput, BaseInputLabel, BaseLoadingCover, BaseMediaItem, BaseMediaLibrary, BaseMediaPreview, BaseMenu, BaseMenuItem, BaseModalCenter, BaseModalSide, BaseNavbar, BaseNavbarItem, BaseNavbarItemContent, BasePagination, BasePanel, BasePassword, BaseProgressCircle, BaseReadMore, BaseSelect, BaseSideNavigation, BaseSideNavigationItem, BaseSkeleton, BaseSwitch, BaseSystemAlert, BaseTabs, BaseTabItem, BaseTagAutocomplete, BaseTagAutocompleteFetch, BaseTable, BaseTableColumn, BaseTextarea, BaseTextareaAutoresize, BaseLayoutStacked, BaseLayoutStackedConfigurable, BaseLayoutSidebar, BaseLayoutSidebarConfigurable, };
69
+ export { BaseActionItem, BaseAlert, BaseApp, BaseAppDialogs, BaseAppNotifications, BaseAutocomplete, BaseAutocompleteFetch, BaseAvatar, BaseBadge, BaseBelongsTo, BaseBoolean, BaseBreadcrumbs, BaseButton, BaseButtonGroup, BaseCard, BaseCardRow, BaseCharacterCounter, BaseClipboard, BaseContainer, BaseCounter, BaseDataIterator, BaseDataTable, BaseDatePicker, BaseDateSelect, BaseDescriptionList, BaseDescriptionListItem, BaseDialog, BaseFilePicker, BaseFileUploader, BaseHasMany, BaseIcon, BaseInput, BaseInputLabel, BaseLoadingCover, BaseMediaItem, BaseMediaLibrary, BaseMediaPreview, BaseMenu, BaseMenuItem, BaseModalCenter, BaseModalSide, BaseNavbar, BaseNavbarItem, BaseNavbarItemContent, BasePagination, BasePanel, BasePassword, BaseProgressCircle, BaseRadioGroup, BaseReadMore, BaseSelect, BaseSideNavigation, BaseSideNavigationItem, BaseSkeleton, BaseSwitch, BaseSystemAlert, BaseTabs, BaseTabItem, BaseTagAutocomplete, BaseTagAutocompleteFetch, BaseTable, BaseTableColumn, BaseTextarea, BaseTextareaAutoresize, BaseLayoutStacked, BaseLayoutStackedConfigurable, BaseLayoutSidebar, BaseLayoutSidebarConfigurable, };
@@ -0,0 +1,7 @@
1
+ import { Ref } from 'vue';
2
+ import { NormalizedOption, Option } from '@/types/types';
3
+ export declare function useHasOptions(modelValue: Ref<Option[] | Option | null | undefined>, options: Ref<Option[]>, labelKey: Ref<string>, valueKey: Ref<string>, multiple?: Ref<boolean>): {
4
+ normalizedOptions: import("vue").ComputedRef<NormalizedOption[]>;
5
+ normalizedModelValue: import("vue").ComputedRef<NormalizedOption | NormalizedOption[] | null>;
6
+ isSelected: (option: NormalizedOption) => boolean;
7
+ };
@@ -14,13 +14,11 @@ export interface DataTableQuery extends Record<string, any> {
14
14
  }
15
15
  export type OptionValue = string | number;
16
16
  export type Option = Record<string, any>;
17
- export type Selection = Option | null | undefined;
18
17
  export type NormalizedOption = {
19
18
  option: Option;
20
19
  value: OptionValue;
21
20
  label: string;
22
21
  };
23
- export type NormalizedSelection = NormalizedOption | null | undefined;
24
22
  export type MediaLibraryPayload = {
25
23
  to_remove: string[];
26
24
  to_add: UploadedFile[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sprintify-ui",
3
- "version": "0.0.32",
3
+ "version": "0.0.34",
4
4
  "scripts": {
5
5
  "build": "rimraf dist && vue-tsc && vite build",
6
6
  "build-fast": "rimraf dist && vite build",
@@ -78,10 +78,7 @@
78
78
  <slot
79
79
  name="option"
80
80
  :option="option.option"
81
- :selected="
82
- normalizedModelValue &&
83
- normalizedModelValue.value == option.value
84
- "
81
+ :selected="isSelected(option)"
85
82
  :active="optionActive && optionActive.value == option.value"
86
83
  >
87
84
  <div
@@ -125,20 +122,16 @@
125
122
 
126
123
  <script lang="ts" setup>
127
124
  import { get } from 'lodash';
128
- import { PropType, Ref } from 'vue';
129
- import {
130
- NormalizedOption,
131
- Option,
132
- Selection,
133
- NormalizedSelection,
134
- } from '@/types/types';
125
+ import { PropType, Ref, ComputedRef } from 'vue';
126
+ import { NormalizedOption, Option } from '@/types/types';
135
127
  import { useInfiniteScroll, useMutationObserver } from '@vueuse/core';
136
128
  import BaseSkeleton from './BaseSkeleton.vue';
129
+ import { useHasOptions } from '@/composables/hasOptions';
137
130
 
138
131
  const props = defineProps({
139
132
  modelValue: {
140
133
  default: undefined,
141
- type: [Object, null] as PropType<Selection | undefined>,
134
+ type: [Object, null] as PropType<Option | null | undefined>,
142
135
  },
143
136
  options: {
144
137
  required: true,
@@ -193,6 +186,19 @@ const selectionIndex = ref(0);
193
186
  const inputElement = ref(null) as Ref<HTMLInputElement | null>;
194
187
  const dropdown = ref(null) as Ref<HTMLElement | null>;
195
188
 
189
+ const hasOptions = useHasOptions(
190
+ computed(() => props.modelValue),
191
+ computed(() => props.options),
192
+ computed(() => props.labelKey),
193
+ computed(() => props.valueKey),
194
+ computed(() => false)
195
+ );
196
+
197
+ const isSelected = hasOptions.isSelected;
198
+ const normalizedOptions = hasOptions.normalizedOptions;
199
+ const normalizedModelValue =
200
+ hasOptions.normalizedModelValue as ComputedRef<NormalizedOption | null>;
201
+
196
202
  onMounted(() => {
197
203
  useInfiniteScroll(
198
204
  dropdown.value,
@@ -211,17 +217,7 @@ const optionActive = computed(() => {
211
217
  );
212
218
  });
213
219
 
214
- const normalizedModelValue = computed(() => {
215
- if (!props.modelValue) {
216
- return null;
217
- }
218
- return {
219
- label: props.modelValue[props.labelKey] as string,
220
- value: props.modelValue[props.valueKey] as string | number,
221
- option: props.modelValue,
222
- };
223
- });
224
-
220
+ // Update the keywords when the model value changes
225
221
  watch(
226
222
  () => normalizedModelValue.value,
227
223
  () => {
@@ -234,16 +230,6 @@ watch(
234
230
  { immediate: true }
235
231
  );
236
232
 
237
- const normalizedOptions = computed(() => {
238
- return props.options.map((option) => {
239
- return {
240
- label: option[props.labelKey] as string,
241
- value: option[props.valueKey] as string | number,
242
- option: option,
243
- } as NormalizedOption;
244
- });
245
- });
246
-
247
233
  const filteredNormalizedOptions = computed((): NormalizedOption[] => {
248
234
  return normalizedOptions.value.filter((option) => {
249
235
  if (props.filter !== undefined) {
@@ -276,6 +262,8 @@ const onTextFocus = () => {
276
262
  emit('focus');
277
263
  };
278
264
 
265
+ // If keywords is changed but no new selection was made,
266
+ // update keywords to original value
279
267
  const onTextBlur = () => {
280
268
  timerId.value = setTimeout(() => {
281
269
  showDropdown.value = false;
@@ -305,7 +293,7 @@ const onTextKeydown = (event: KeyboardEvent) => {
305
293
  }
306
294
 
307
295
  if (key === 'ArrowDown') {
308
- if (selectionIndex.value < props.options.length - 1) {
296
+ if (selectionIndex.value < filteredNormalizedOptions.value.length - 1) {
309
297
  selectionIndex.value++;
310
298
  } else {
311
299
  selectionIndex.value = 0;
@@ -317,7 +305,10 @@ const onTextKeydown = (event: KeyboardEvent) => {
317
305
  if (selectionIndex.value > 0) {
318
306
  selectionIndex.value--;
319
307
  } else {
320
- selectionIndex.value = Math.max(0, props.options.length - 1);
308
+ selectionIndex.value = Math.max(
309
+ 0,
310
+ filteredNormalizedOptions.value.length - 1
311
+ );
321
312
  }
322
313
  return;
323
314
  }
@@ -333,9 +324,7 @@ const onTextKeydown = (event: KeyboardEvent) => {
333
324
 
334
325
  const optionClass = (option: NormalizedOption) => {
335
326
  const active = optionActive.value && optionActive.value.value == option.value;
336
- const selected =
337
- normalizedModelValue.value &&
338
- normalizedModelValue.value.value == option.value;
327
+ const selected = isSelected(option);
339
328
 
340
329
  if (selected) {
341
330
  if (active) {
@@ -359,12 +348,12 @@ const clear = () => {
359
348
  inputElement.value?.focus();
360
349
  };
361
350
 
362
- const onSelect = (normalizedModelValue: NormalizedSelection) => {
351
+ const onSelect = (normalizedModelValue: Option | null | undefined) => {
363
352
  update(normalizedModelValue);
364
353
  inputElement.value?.blur();
365
354
  };
366
355
 
367
- const update = (normalizedSelection: NormalizedSelection) => {
356
+ const update = (normalizedSelection: Option | null | undefined) => {
368
357
  const selection = normalizedSelection ? normalizedSelection.option : null;
369
358
  if (normalizedSelection) {
370
359
  setKeywordsWithoutEvent(normalizedSelection.label);
@@ -61,14 +61,14 @@
61
61
  import { config } from '../';
62
62
  import { debounce } from 'lodash';
63
63
  import { PropType, Ref } from 'vue';
64
- import { Option, Selection } from '@/types/types';
64
+ import { Option } from '@/types/types';
65
65
  import { RouteLocationRaw } from 'vue-router';
66
66
  import BaseAutocomplete from './BaseAutocomplete.vue';
67
67
 
68
68
  const props = defineProps({
69
69
  modelValue: {
70
70
  default: undefined,
71
- type: [Object, null] as PropType<Selection | undefined>,
71
+ type: [Object, null] as PropType<Option | null | undefined>,
72
72
  },
73
73
  url: {
74
74
  required: true,
@@ -24,12 +24,12 @@
24
24
  </template>
25
25
 
26
26
  <script lang="ts" setup>
27
- import { Selection } from '@/types/types';
28
27
  import { PropType } from 'vue';
29
28
  import { RouteLocationRaw } from 'vue-router';
30
29
  import { AxiosResponse } from 'axios';
31
30
  import { config } from '@/index';
32
31
  import BaseAutocompleteFetch from './BaseAutocompleteFetch.vue';
32
+ import { Option } from '@/types/types';
33
33
 
34
34
  const props = defineProps({
35
35
  modelValue: {
@@ -72,7 +72,7 @@ const props = defineProps({
72
72
  },
73
73
  currentModel: {
74
74
  default: null,
75
- type: [Object, null] as PropType<Selection>,
75
+ type: [Object, null] as PropType<Option | null>,
76
76
  },
77
77
  createNewUrl: {
78
78
  default: '',
@@ -120,7 +120,7 @@ watch(
120
120
  { immediate: true }
121
121
  );
122
122
 
123
- function onUpdate(newModel: Selection) {
123
+ function onUpdate(newModel: Option | null) {
124
124
  if (!newModel) {
125
125
  model.value = null;
126
126
  emit('update:modelValue', null);
@@ -70,17 +70,14 @@ export const SlotOption = (args) => ({
70
70
  v-bind="args"
71
71
  v-model="value"
72
72
  :options="options"
73
+ button-class="btn btn-xs"
74
+ button-selected-class="btn-black"
73
75
  >
74
- <template #option="{ option, isActive, onSelect }">
75
- <button
76
- class="btn btn-xs flex items-center space-x-1 font-semibold"
77
- :class="[isActive(option) ? 'btn-black' : '']"
78
- type="button"
79
- @click="onSelect(option)"
80
- >
76
+ <template #option="{ option }">
77
+ <div class="flex items-center space-x-1 font-semibold">
81
78
  <div class="w-3 h-3 rounded" :style="{ backgroundColor: option.value }"></div>
82
79
  <div>{{ option.label }}</div>
83
- </button>
80
+ </div>
84
81
  </template>
85
82
  </BaseButtonGroup>
86
83
  `,
@@ -5,24 +5,24 @@
5
5
  :key="option.value"
6
6
  :style="{ padding: spacing }"
7
7
  >
8
- <slot
9
- name="option"
10
- :is-active="isActive"
11
- :on-select="onSelect"
12
- :option="option"
8
+ <button
9
+ :type="buttonType"
10
+ :disabled="disabled"
11
+ :class="[
12
+ buttonClass,
13
+ isSelected(option) ? buttonSelectedClass : buttonUnselectedClass,
14
+ ]"
15
+ @click="onSelect(option)"
13
16
  >
14
- <button
15
- :type="buttonType"
17
+ <slot
18
+ name="option"
19
+ :selected="isSelected(option)"
20
+ :option="option"
16
21
  :disabled="disabled"
17
- :class="[
18
- buttonClass,
19
- isActive(option) ? buttonActiveClass : buttonInactiveClass,
20
- ]"
21
- @click="onSelect(option)"
22
22
  >
23
23
  {{ option.label }}
24
- </button>
25
- </slot>
24
+ </slot>
25
+ </button>
26
26
  </div>
27
27
  </div>
28
28
  </template>
@@ -30,7 +30,8 @@
30
30
  <script lang="ts" setup>
31
31
  import { PropType } from 'vue';
32
32
  import { NormalizedOption, Option } from '@/types/types';
33
- import { cloneDeep, isArray, isObject } from 'lodash';
33
+ import { cloneDeep, isArray } from 'lodash';
34
+ import { useHasOptions } from '@/composables/hasOptions';
34
35
 
35
36
  const props = defineProps({
36
37
  modelValue: {
@@ -55,11 +56,11 @@ const props = defineProps({
55
56
  default: 'btn btn-sm',
56
57
  type: String,
57
58
  },
58
- buttonActiveClass: {
59
+ buttonSelectedClass: {
59
60
  default: 'btn-primary',
60
61
  type: String,
61
62
  },
62
- buttonInactiveClass: {
63
+ buttonUnselectedClass: {
63
64
  default: '',
64
65
  type: String,
65
66
  },
@@ -87,57 +88,14 @@ const props = defineProps({
87
88
 
88
89
  const emit = defineEmits(['update:modelValue']);
89
90
 
90
- const normalizedModelValue = computed(
91
- (): NormalizedOption[] | NormalizedOption | null => {
92
- if (props.multiple) {
93
- if (!isArray(props.modelValue)) {
94
- return [];
95
- }
96
- return props.modelValue.map((option) => {
97
- return {
98
- label: option[props.labelKey] as string,
99
- value: option[props.valueKey] as string | number,
100
- option: option,
101
- } as NormalizedOption;
102
- });
103
- } else {
104
- if (!isObject(props.modelValue)) {
105
- return null;
106
- }
107
-
108
- return {
109
- label: props.modelValue[props.labelKey as never] as string,
110
- value: props.modelValue[props.valueKey as never] as string | number,
111
- option: props.modelValue,
112
- } as NormalizedOption;
113
- }
114
- }
91
+ const { normalizedOptions, normalizedModelValue, isSelected } = useHasOptions(
92
+ computed(() => props.modelValue),
93
+ computed(() => props.options),
94
+ computed(() => props.labelKey),
95
+ computed(() => props.valueKey),
96
+ computed(() => props.multiple)
115
97
  );
116
98
 
117
- const normalizedOptions = computed((): NormalizedOption[] => {
118
- return props.options.map((option) => {
119
- return {
120
- label: option[props.labelKey] as string,
121
- value: option[props.valueKey] as string | number,
122
- option: option,
123
- } as NormalizedOption;
124
- });
125
- });
126
-
127
- function isActive(option: NormalizedOption): boolean {
128
- if (isArray(normalizedModelValue.value)) {
129
- return normalizedModelValue.value.some((modelValue) => {
130
- return modelValue.value === option.value;
131
- });
132
- }
133
-
134
- if (isObject(normalizedModelValue.value)) {
135
- return normalizedModelValue.value.value == option.value;
136
- }
137
-
138
- return false;
139
- }
140
-
141
99
  function onSelect(option: NormalizedOption) {
142
100
  if (props.multiple) {
143
101
  let newModalValue = [] as NormalizedOption[];
@@ -0,0 +1,88 @@
1
+ import BaseRadioGroup from './BaseRadioGroup.vue';
2
+
3
+ export default {
4
+ title: 'Form/BaseRadioGroup',
5
+ component: BaseRadioGroup,
6
+ argTypes: {},
7
+ args: {
8
+ name: 'character',
9
+ labelKey: 'label',
10
+ valueKey: 'value',
11
+ options: [
12
+ { label: 'Dark Vader', value: 'dark_vader' },
13
+ { label: 'Darth Maul', value: 'darth_maul' },
14
+ { label: 'Dark Sidious', value: 'dark_sidious' },
15
+ { label: 'Obi Wan Kenobi', value: 'obiwan' },
16
+ { label: 'Anakin Skywalker', value: 'anakin' },
17
+ { label: 'Mace Windu', value: 'windu' },
18
+ ],
19
+ },
20
+ decorators: [() => ({ template: '<div class="mb-36"><story/></div>' })],
21
+ };
22
+
23
+ const Template = (args) => ({
24
+ components: { BaseRadioGroup },
25
+ setup() {
26
+ const value = ref(null);
27
+ function onSubmit() {
28
+ alert('submit');
29
+ }
30
+ return { args, value, onSubmit };
31
+ },
32
+ template: `
33
+ <form @submit.prevent="onSubmit">
34
+ <BaseRadioGroup v-model="value" v-bind="args"></BaseRadioGroup>
35
+ <button type="submit" class="btn btn-primary mt-4">Submit</button>
36
+ <p class="mt-5 text-sm">Value: <span class="bg-slate-200 font-mono px-1 py-px rounded">{{ value ?? 'NULL' }}</span></p>
37
+ </form>
38
+ `,
39
+ });
40
+
41
+ export const Demo = Template.bind({});
42
+ Demo.args = {};
43
+
44
+ export const Required = Template.bind({});
45
+ Required.args = {
46
+ required: true,
47
+ };
48
+
49
+ export const Disabled = Template.bind({});
50
+ Disabled.args = {
51
+ disabled: true,
52
+ modelValue: { label: 'Dark Maul', value: 'darth_maul' },
53
+ };
54
+
55
+ export const SlotOption = (args) => ({
56
+ components: { BaseRadioGroup },
57
+ setup() {
58
+ const value = ref(null);
59
+
60
+ const options = [
61
+ { label: 'Red', value: 'red' },
62
+ { label: 'Blue', value: 'blue' },
63
+ { label: 'Green', value: 'green' },
64
+ { label: 'Black', value: 'black' },
65
+ { label: 'Gray', value: 'gray' },
66
+ ];
67
+
68
+ return { value, options, args };
69
+ },
70
+ template: `
71
+ <BaseRadioGroup
72
+ v-bind="args"
73
+ v-model="value"
74
+ :options="options"
75
+ label-class="flex space-x-2 items-start"
76
+ >
77
+ <template #option="{ option }">
78
+ <div>
79
+ <div class="flex items-center space-x-1">
80
+ <div style="height: 10px; width: 10px; border-radius: 4px;" :style="{ backgroundColor: option.value }"></div>
81
+ <div class="leading-none font-medium text-sm">{{ option.label }}</div>
82
+ </div>
83
+ <p class="text-slate-500 text-xs leading-tight">{{ option.value }}</p>
84
+ </div>
85
+ </template>
86
+ </BaseRadioGroup>
87
+ `,
88
+ });
@@ -0,0 +1,84 @@
1
+ <template>
2
+ <div>
3
+ <ul class="space-y-3">
4
+ <li v-for="option in normalizedOptions" :key="option.value">
5
+ <label
6
+ :for="name + '-' + option.value"
7
+ class="cursor-pointer"
8
+ :class="[labelClass, disabled ? 'cursor-not-allowed opacity-50' : '']"
9
+ >
10
+ <input
11
+ :id="name + '-' + option.value"
12
+ type="radio"
13
+ :name="name"
14
+ :checked="isSelected(option)"
15
+ :required="required"
16
+ :disabled="disabled"
17
+ :value="option.value"
18
+ :class="inputClass"
19
+ @input="emit('update:modelValue', option.option)"
20
+ />
21
+
22
+ <slot name="option" :option="option">
23
+ <span class="text-sm">{{ option.label }}</span>
24
+ </slot>
25
+ </label>
26
+ </li>
27
+ </ul>
28
+ </div>
29
+ </template>
30
+
31
+ <script lang="ts" setup>
32
+ import { PropType } from 'vue';
33
+ import { Option } from '@/types/types';
34
+ import { useHasOptions } from '@/composables/hasOptions';
35
+
36
+ const props = defineProps({
37
+ modelValue: {
38
+ default: undefined,
39
+ type: [Object, null] as PropType<Option | undefined>,
40
+ },
41
+ name: {
42
+ required: true,
43
+ type: String,
44
+ },
45
+ required: {
46
+ default: false,
47
+ type: Boolean,
48
+ },
49
+ disabled: {
50
+ default: false,
51
+ type: Boolean,
52
+ },
53
+ options: {
54
+ required: true,
55
+ type: Array as PropType<Option[]>,
56
+ },
57
+ labelKey: {
58
+ required: true,
59
+ type: String,
60
+ },
61
+ valueKey: {
62
+ required: true,
63
+ type: String,
64
+ },
65
+ labelClass: {
66
+ default: 'flex space-x-2',
67
+ type: String,
68
+ },
69
+ inputClass: {
70
+ default: 'mt-0.5 border-slate-300',
71
+ type: String,
72
+ },
73
+ });
74
+
75
+ const emit = defineEmits(['update:modelValue']);
76
+
77
+ const { normalizedOptions, isSelected } = useHasOptions(
78
+ computed(() => props.modelValue),
79
+ computed(() => props.options),
80
+ computed(() => props.labelKey),
81
+ computed(() => props.valueKey),
82
+ computed(() => false)
83
+ );
84
+ </script>
@@ -64,16 +64,14 @@ export const SlotOption = (args) => ({
64
64
  v-model="value"
65
65
  :options="options"
66
66
  >
67
- <template #option="{ option, active, selected }">
67
+ <template #option="{ option, active }">
68
68
  <div
69
69
  class="rounded px-2 font-semibold py-1 text-sm"
70
70
  :class="{
71
- 'hover:bg-slate-100': !active && !selected,
72
- 'bg-slate-200 hover:bg-slate-300': active && !selected,
73
- 'bg-blue-500 text-white hover:bg-blue-600': !active && selected,
74
- 'bg-blue-600 text-white hover:bg-blue-700': active && selected,
71
+ 'hover:bg-slate-100': !active,
72
+ 'bg-slate-200 hover:bg-slate-300': active,
75
73
  }"
76
- :style="{ color: selected ? '' : option.value }"
74
+ :style="{ color: option.value }"
77
75
  >
78
76
  {{ option.label }}
79
77
  </div>