vunor 0.0.2

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 (60) hide show
  1. package/README.md +33 -0
  2. package/dist/theme.d.ts +133 -0
  3. package/dist/theme.mjs +1149 -0
  4. package/dist/vite.d.ts +5 -0
  5. package/dist/vite.mjs +13 -0
  6. package/package.json +83 -0
  7. package/src/components/AppLayout/AppLayout.vue +114 -0
  8. package/src/components/Button/Button.vue +49 -0
  9. package/src/components/Button/ButtonBase.vue +43 -0
  10. package/src/components/Button/index.ts +1 -0
  11. package/src/components/Button/shortcuts.ts +29 -0
  12. package/src/components/Card/Card.vue +47 -0
  13. package/src/components/Card/CardHeader.vue +26 -0
  14. package/src/components/Card/CardInner.vue +5 -0
  15. package/src/components/Card/index.ts +3 -0
  16. package/src/components/Card/pi.ts +46 -0
  17. package/src/components/Card/shortcuts.ts +19 -0
  18. package/src/components/Checkbox/Checkbox.vue +59 -0
  19. package/src/components/Checkbox/index.ts +1 -0
  20. package/src/components/Checkbox/shortcuts.ts +27 -0
  21. package/src/components/Combobox/Combobox.vue +502 -0
  22. package/src/components/Combobox/index.ts +2 -0
  23. package/src/components/Combobox/shortcuts.ts +12 -0
  24. package/src/components/Combobox/types.ts +33 -0
  25. package/src/components/Icon/Icon.vue +9 -0
  26. package/src/components/Icon/index.ts +1 -0
  27. package/src/components/Input/Input.vue +109 -0
  28. package/src/components/Input/InputShell.vue +133 -0
  29. package/src/components/Input/index.ts +4 -0
  30. package/src/components/Input/pi.ts +18 -0
  31. package/src/components/Input/types.ts +52 -0
  32. package/src/components/Input/utils.ts +108 -0
  33. package/src/components/Label/Label.vue +6 -0
  34. package/src/components/Label/index.ts +1 -0
  35. package/src/components/Loading/LoadingIndicator.vue +23 -0
  36. package/src/components/Loading/index.ts +1 -0
  37. package/src/components/Loading/shortcuts.ts +9 -0
  38. package/src/components/Menu/Menu.vue +100 -0
  39. package/src/components/Menu/MenuItem.vue +20 -0
  40. package/src/components/Menu/index.ts +2 -0
  41. package/src/components/Menu/shortcuts.ts +6 -0
  42. package/src/components/OverflowContainer/OverflowContainer.vue +120 -0
  43. package/src/components/OverflowContainer/index.ts +1 -0
  44. package/src/components/Pagination/Pagination.vue +71 -0
  45. package/src/components/Popover/Popover.vue +58 -0
  46. package/src/components/Popover/index.ts +1 -0
  47. package/src/components/RadioGroup/RadioGroup.vue +83 -0
  48. package/src/components/RadioGroup/index.ts +1 -0
  49. package/src/components/RadioGroup/shortcuts.ts +34 -0
  50. package/src/components/Select/Select.vue +93 -0
  51. package/src/components/Select/SelectBase.vue +148 -0
  52. package/src/components/Select/index.ts +3 -0
  53. package/src/components/Select/shortcuts.ts +30 -0
  54. package/src/components/Select/types.ts +30 -0
  55. package/src/components/Slider/Slider.vue +73 -0
  56. package/src/components/Slider/index.ts +1 -0
  57. package/src/components/Slider/shortcuts.ts +23 -0
  58. package/src/components/shortcuts.ts +21 -0
  59. package/src/components/utils/index.ts +1 -0
  60. package/src/components/utils/provide-inject.ts +39 -0
@@ -0,0 +1,71 @@
1
+ <script setup lang="ts">
2
+ defineProps<{
3
+ total: number
4
+ defaultPage?: number
5
+ showEdges?: boolean
6
+ siblingCount?: number
7
+ itemsPerPage?: number
8
+ showArrows?: boolean
9
+ }>()
10
+ const modelValue = defineModel<number>()
11
+ const emit = defineEmits<{
12
+ (name: 'update:page', v: number): void
13
+ }>()
14
+ </script>
15
+
16
+ <template>
17
+ <PaginationRoot
18
+ :total
19
+ :itemsPerPage
20
+ :sibling-count
21
+ :show-edges
22
+ :default-page
23
+ v-model:page="modelValue"
24
+ @update:page="emit('update:page', $event)"
25
+ >
26
+ <PaginationList v-slot="{ items }" class="flex items-center gap-1">
27
+ <PaginationFirst
28
+ v-if="!showEdges && showArrows"
29
+ class="size-fingertip flex items-center justify-center btn btn-square c8-flat disabled:opacity-30"
30
+ >
31
+ <VuIcon name="i--chevron-left-2" class="btn-icon btn-icon-left" />
32
+ </PaginationFirst>
33
+ <PaginationPrev
34
+ v-if="showArrows"
35
+ class="size-fingertip flex items-center justify-center mr-4 btn btn-square c8-flat disabled:opacity-30"
36
+ >
37
+ <VuIcon name="i--chevron-left" class="btn-icon btn-icon-left" />
38
+ </PaginationPrev>
39
+ <template v-for="(page, index) in items">
40
+ <PaginationListItem
41
+ v-if="page.type === 'page'"
42
+ :key="index"
43
+ class="size-fingertip btn btn-square c8-flat"
44
+ :value="page.value"
45
+ >
46
+ {{ page.value }}
47
+ </PaginationListItem>
48
+ <PaginationEllipsis
49
+ v-else
50
+ :key="page.type"
51
+ :index="index"
52
+ class="size-fingertip flex items-center justify-center"
53
+ >
54
+ &#8230;
55
+ </PaginationEllipsis>
56
+ </template>
57
+ <PaginationNext
58
+ v-if="showArrows"
59
+ class="size-fingertip flex items-center justify-center ml-4 btn btn-square c8-flat disabled:opacity-30"
60
+ >
61
+ <VuIcon name="i--chevron-right" class="btn-icon btn-icon-left" />
62
+ </PaginationNext>
63
+ <PaginationLast
64
+ v-if="!showEdges && showArrows"
65
+ class="size-fingertip flex items-center justify-center btn btn-square c8-flat disabled:opacity-30"
66
+ >
67
+ <VuIcon name="i--chevron-right-2" class="btn-icon btn-icon-left" />
68
+ </PaginationLast>
69
+ </PaginationList>
70
+ </PaginationRoot>
71
+ </template>
@@ -0,0 +1,58 @@
1
+ <script setup lang="ts">
2
+ import type { PopoverContentProps } from 'radix-vue'
3
+
4
+ const props = defineProps<
5
+ PopoverContentProps & {
6
+ class?: string | Record<string, boolean>
7
+ icon?: string
8
+ label?: string
9
+ asLink?: boolean
10
+ }
11
+ >()
12
+
13
+ const popupContentProps = computed(() => {
14
+ const obj = {} as PopoverContentProps
15
+ for (const [key, value] of Object.entries(props)) {
16
+ if (!['class', 'label', 'icon'].includes(key)) {
17
+ obj[key as keyof PopoverContentProps] = value as undefined
18
+ }
19
+ }
20
+ return obj
21
+ })
22
+ const modelValue = defineModel<boolean>()
23
+ function open() {
24
+ modelValue.value = true
25
+ }
26
+ function close() {
27
+ modelValue.value = false
28
+ }
29
+ </script>
30
+
31
+ <template>
32
+ <PopoverRoot as-child v-model:open="modelValue">
33
+ <PopoverTrigger as-child>
34
+ <slot :isOpen="modelValue" :close :open
35
+ ><VuButton
36
+ :as-link
37
+ :icon="icon || 'i--more-vert'"
38
+ :class
39
+ :label
40
+ :data-active="modelValue ? '' : undefined"
41
+ /></slot>
42
+ </PopoverTrigger>
43
+ <PopoverPortal>
44
+ <PopoverContent
45
+ v-bind="popupContentProps"
46
+ :style="{
47
+ 'max-height': 'var(--radix-popper-available-height)',
48
+ 'overflow': 'auto',
49
+ }"
50
+ as-child
51
+ >
52
+ <slot name="content" :isOpen="modelValue" :close :open>
53
+ <div></div>
54
+ </slot>
55
+ </PopoverContent>
56
+ </PopoverPortal>
57
+ </PopoverRoot>
58
+ </template>
@@ -0,0 +1 @@
1
+ export { default as VuPopover } from './Popover.vue'
@@ -0,0 +1,83 @@
1
+ <script setup lang="ts" generic="T extends { value: string; label: string; disabled?: boolean }">
2
+ import { useId } from 'radix-vue'
3
+
4
+ const props = defineProps<{
5
+ items: (string | T)[]
6
+ defaultValue?: string
7
+ label?: string
8
+ labelVisible?: boolean
9
+ row?: boolean
10
+ disabledValues?: string[]
11
+ disabled?: boolean
12
+ class?: string | Record<string, boolean>
13
+ error?: string | boolean
14
+ verticalMiddle?: boolean
15
+ reverse?: boolean
16
+ }>()
17
+
18
+ const _items = computed<T[]>(() => {
19
+ return props.items.map(item =>
20
+ typeof item === 'string' ? ({ value: item, label: item } as T) : item
21
+ )
22
+ })
23
+
24
+ const id = useId()
25
+
26
+ const modelValue = defineModel<string>()
27
+
28
+ function isDisabled(val: string) {
29
+ return props.disabledValues?.includes(val)
30
+ }
31
+ </script>
32
+ <template>
33
+ <div class="rb-container" :class>
34
+ <label v-if="!!label && labelVisible" class="rb-label">{{ label }}</label>
35
+ <RadioGroupRoot
36
+ v-model="modelValue"
37
+ class="rb-root"
38
+ :class="{ 'rb-row': row }"
39
+ :default-value
40
+ :aria-label="label"
41
+ :disabled
42
+ :data-error="!!error"
43
+ :aria-disabled="disabled"
44
+ >
45
+ <div
46
+ class="rb-item-wrapper"
47
+ :class="{
48
+ 'items-center': verticalMiddle,
49
+ 'items-start': !verticalMiddle,
50
+ 'flex-row-reverse': reverse,
51
+ }"
52
+ v-for="item of _items"
53
+ >
54
+ <RadioGroupItem
55
+ :id="id + '-' + item.value"
56
+ class="rb-item"
57
+ :data-error="!!error"
58
+ :value="item.value"
59
+ :disabled="disabled || item.disabled || isDisabled(item.value)"
60
+ :aria-disabled="disabled || item.disabled || isDisabled(item.value)"
61
+ >
62
+ <RadioGroupIndicator class="rb-item-indicator" />
63
+ </RadioGroupItem>
64
+ <label
65
+ class="rb-item-label"
66
+ :for="id + '-' + item.value"
67
+ :aria-disabled="disabled || item.disabled || isDisabled(item.value)"
68
+ >
69
+ <slot v-bind="item">
70
+ {{ item.label }}
71
+ </slot>
72
+ </label>
73
+ </div>
74
+ </RadioGroupRoot>
75
+ <div
76
+ v-if="!!error && typeof error === 'string'"
77
+ class="text-caption text-error-500 text-mt-0"
78
+ :class="{ 'text-right': reverse }"
79
+ >
80
+ {{ error }}
81
+ </div>
82
+ </div>
83
+ </template>
@@ -0,0 +1 @@
1
+ export { default as VuRadioGroup } from './RadioGroup.vue'
@@ -0,0 +1,34 @@
1
+ import { scFromObject } from '../../theme/utils/shortcut-obj'
2
+
3
+ export const radioShortcuts = {
4
+ 'rb-container': scFromObject({
5
+ '': 'flex flex-col gap-$s text-body',
6
+ }),
7
+ 'rb-label': scFromObject({
8
+ '': 'text-label text-grey-400',
9
+ }),
10
+ 'rb-root': scFromObject({
11
+ '': 'flex gap-x-$l gap-y-$m',
12
+ '[&.rb-row]:': 'flex-wrap',
13
+ 'not-[.rb-row]:': 'flex-col',
14
+ }),
15
+ 'rb-item-wrapper': scFromObject({
16
+ '': 'flex',
17
+ }),
18
+ 'rb-item': scFromObject({
19
+ '': 'select-none shrink-0 current-bg-scope-color-500 bg-current/0 size-1.25em rounded-full cursor-default current-border-grey-500 border-current/40 border-[0.16em] transition-none backdrop-blur-sm',
20
+ "data-[state=checked]:not-[[data-error='true']]:":
21
+ 'current-border-scope-color-500 border-current',
22
+ 'data-[state=checked]:': 'bg-current',
23
+ 'active:enabled:': 'bg-current/20',
24
+ 'aria-[disabled=true]:': 'scope-grey opacity-50 cursor-not-allowed',
25
+ 'data-[error=true]:': 'current-border-error-500 current-bg-error-500',
26
+ }),
27
+ 'rb-item-indicator': scFromObject({
28
+ '': "flex items-center justify-center w-full h-full rounded-full relative after:content-[''] after:block after:size-[0.5em] after:rounded-[50%] after:bg-white animate-zoom-in animate-duration-100",
29
+ }),
30
+ 'rb-item-label': scFromObject({
31
+ '': 'select-none px-$s text-body leading-none lh-1.25em',
32
+ 'aria-[disabled=true]:': 'scope-grey opacity-50 cursor-not-allowed',
33
+ }),
34
+ }
@@ -0,0 +1,93 @@
1
+ <script setup lang="ts" generic="T extends TSelectItem">
2
+ import type { TInputProps, TInputShellProps, TInputEmits } from '../Input/types'
3
+ import { useInputShellProps, useInputProps } from '../Input/utils'
4
+ import type { TSelectBaseProps, TSelectItem } from './types'
5
+
6
+ type Props = Omit<TInputProps & TInputShellProps, 'active'> & TSelectBaseProps<T>
7
+
8
+ const props = withDefaults(defineProps<Props>(), {
9
+ sideOffset: 2,
10
+ })
11
+ defineEmits<TInputEmits>()
12
+
13
+ const forwardProps = computed(() => {
14
+ if (props.groupItem) {
15
+ return useInputShellProps()?.value
16
+ }
17
+ return useInputProps()?.value
18
+ })
19
+
20
+ const modelValue = defineModel<string>()
21
+ </script>
22
+
23
+ <template>
24
+ <VuSelectBase
25
+ v-model="modelValue"
26
+ :disabled-values
27
+ :popup-class
28
+ :popup-round
29
+ :value-class
30
+ :icon-class
31
+ :class
32
+ :items
33
+ :required
34
+ :disabled
35
+ :placeholder
36
+ :defaultValue
37
+ :popupPosition
38
+ :side
39
+ :sideOffset
40
+ :sticky
41
+ :updatePositionStrategy
42
+ v-slot="s"
43
+ >
44
+ <SelectTrigger as-child v-if="groupItem">
45
+ <!-- prettier-ignore-attribute v-model -->
46
+ <VuInputShell
47
+ class="cursor-pointer select-none"
48
+ v-bind="forwardProps"
49
+ :icon-append="typeof iconAppend === 'string' ? iconAppend : s.icon"
50
+ :model-value="(s.displayItem?.label || s.displayItem?.value) as string"
51
+ :active="s.open"
52
+ readonly
53
+ @click="s.openPopup"
54
+ tabindex="-1"
55
+ @focus="$event.target.querySelector('input')?.focus()"
56
+ >
57
+ <template #overlay>
58
+ <SelectValue class="absolute left-0 right-0 h-0 invisible" />
59
+ </template>
60
+ </VuInputShell>
61
+ </SelectTrigger>
62
+ <!-- prettier-ignore-attribute v-model -->
63
+ <VuInput
64
+ v-else
65
+ class="select-none"
66
+ v-bind="forwardProps"
67
+ :icon-append="typeof iconAppend === 'string' ? iconAppend : s.icon"
68
+ :model-value="(s.displayItem?.label || s.displayItem?.value) as string"
69
+ :active="s.open"
70
+ :stack-label
71
+ readonly
72
+ @click="s.openPopup"
73
+ v-slot="shellProps"
74
+ >
75
+ <SelectTrigger as-child>
76
+ <!-- prettier-ignore-attribute v-model -->
77
+ <VuInputShell
78
+ class="cursor-pointer select-none"
79
+ v-bind="shellProps"
80
+ :model-value="(s.displayItem?.label || s.displayItem?.value) as string"
81
+ readonly
82
+ type="text"
83
+ tabindex="-1"
84
+ @focus="$event.target.querySelector('input')?.focus()"
85
+ >
86
+ <template #overlay>
87
+ <SelectValue class="absolute left-[-1px] right-0 h-0 invisible" />
88
+ </template>
89
+ </VuInputShell>
90
+ </SelectTrigger>
91
+ </VuInput>
92
+ </VuSelectBase>
93
+ </template>
@@ -0,0 +1,148 @@
1
+ <script setup lang="ts" generic="T extends TSelectItem">
2
+ import type { TSelectBaseProps, TSelectItem } from './types'
3
+
4
+ const props = defineProps<TSelectBaseProps<T>>()
5
+
6
+ const open = defineModel<boolean>('open')
7
+ const modelValue = defineModel<string>()
8
+
9
+ const groups = computed(() => {
10
+ if (Array.isArray(props.items)) {
11
+ return [
12
+ {
13
+ grp: '',
14
+ items: props.items.map(item =>
15
+ typeof item === 'string' ? ({ value: item, label: item } as T) : item
16
+ ),
17
+ },
18
+ ]
19
+ } else {
20
+ const r = [] as { grp: string; items: T[] }[]
21
+ for (const [key, val] of Object.entries(props.items)) {
22
+ r.push({
23
+ grp: key,
24
+ items: val.map(item =>
25
+ typeof item === 'string' ? ({ value: item, label: item } as T) : item
26
+ ),
27
+ })
28
+ }
29
+ return r
30
+ }
31
+ })
32
+
33
+ const flatItems = computed(() => {
34
+ const r = new Map<string | null | undefined, T>()
35
+ for (const grp of groups.value) {
36
+ for (const item of grp.items) {
37
+ r.set(item.value, item)
38
+ }
39
+ }
40
+ return r
41
+ })
42
+
43
+ function isItemDisabled(val: string | null | undefined) {
44
+ return props.disabledValues?.includes(val)
45
+ }
46
+
47
+ async function openPopup() {
48
+ if (!props.disabled) {
49
+ open.value = !open.value
50
+ }
51
+ }
52
+
53
+ const displayItem = computed(() => flatItems.value.get(modelValue.value))
54
+
55
+ function getSearchValue(item: T) {
56
+ return item.search || item.label?.replace(/[^\p{L}\p{N}\p{P}\p{M}\p{Zs}]/gu, '').trim()
57
+ }
58
+ </script>
59
+
60
+ <template>
61
+ <!-- prettier-ignore-attribute default-value -->
62
+ <SelectRoot
63
+ v-model="modelValue"
64
+ v-model:open="open"
65
+ :disabled
66
+ :required
67
+ :default-value="defaultValue as string"
68
+ >
69
+ <slot :displayItem="displayItem" :value="modelValue" :openPopup :open :icon="'i--chevron-down'">
70
+ <SelectTrigger :class>
71
+ <VuIcon
72
+ v-if="!!displayItem?.icon"
73
+ :name="displayItem.icon"
74
+ class="inline-block vertical-middle mr-$xs"
75
+ style="color: currentColor"
76
+ />
77
+ <SelectValue :placeholder :class="valueClass">
78
+ {{ displayItem?.label }}
79
+ </SelectValue>
80
+ <VuIcon name="i--chevron-down" :class="iconClass" />
81
+ </SelectTrigger>
82
+ </slot>
83
+
84
+ <SelectPortal>
85
+ <SelectContent
86
+ class="select-content group"
87
+ :style="{
88
+ 'min-width': 'var(--radix-popper-anchor-width)',
89
+ 'max-height': popupPosition === 'popper' ? 'var(--radix-popper-available-height)' : '',
90
+ 'overflow': popupPosition === 'popper' ? 'auto' : '',
91
+ }"
92
+ :data-design="popupRound ? 'round' : undefined"
93
+ :class="popupClass"
94
+ :position="popupPosition"
95
+ :side
96
+ :side-offset
97
+ :sticky
98
+ :update-position-strategy
99
+ >
100
+ <SelectScrollUpButton class="select-scroll-btn">
101
+ <VuIcon name="i--chevron-up" class="size-1em" />
102
+ </SelectScrollUpButton>
103
+
104
+ <SelectViewport>
105
+ <template v-for="(g, grpIndex) of groups" :key="grpIndex">
106
+ <SelectSeparator v-if="grpIndex > 0" class="select-separator" />
107
+ <SelectLabel class="select-grp-label" v-if="!!g.grp">
108
+ <slot name="group" v-bind="g">
109
+ <span>{{ g.grp }}</span>
110
+ </slot>
111
+ </SelectLabel>
112
+ <SelectGroup>
113
+ <SelectItem
114
+ v-for="(item, index) in g.items"
115
+ :key="index"
116
+ class="select-item relative"
117
+ :value="String(item.value)"
118
+ :disabled="disabled || item.disabled || isItemDisabled(item.value)"
119
+ :aria-disabled="disabled || item.disabled || isItemDisabled(item.value)"
120
+ >
121
+ <SelectItemText class="absolute">
122
+ <span class="hidden">
123
+ {{ getSearchValue(item) }}
124
+ </span>
125
+ </SelectItemText>
126
+ <span>
127
+ <slot name="item" v-bind="item">
128
+ <VuIcon
129
+ v-if="!!item.icon"
130
+ :name="item.icon"
131
+ class="inline-block vertical-middle mr-$xs"
132
+ style="color: currentColor"
133
+ />
134
+ {{ item.label }}
135
+ </slot>
136
+ </span>
137
+ </SelectItem>
138
+ </SelectGroup>
139
+ </template>
140
+ </SelectViewport>
141
+
142
+ <SelectScrollDownButton class="select-scroll-btn">
143
+ <VuIcon name="i--chevron-down" class="size-1em" />
144
+ </SelectScrollDownButton>
145
+ </SelectContent>
146
+ </SelectPortal>
147
+ </SelectRoot>
148
+ </template>
@@ -0,0 +1,3 @@
1
+ export { default as VuSelect } from './Select.vue'
2
+ export { default as VuSelectBase } from './SelectBase.vue'
3
+ export * from './types'
@@ -0,0 +1,30 @@
1
+ import { scFromObject } from '../../theme/utils/shortcut-obj'
2
+
3
+ export const selectShortcuts = {
4
+ 'select-content': scFromObject({
5
+ '': 'min-w-[60px] rounded-base surface-0 bg-current/70 backdrop-blur-xl overflow-hidden shadow-xl z-[100] current-border-grey-400 border-current/20 ',
6
+ 'data-[design=round]:': 'rounded-fingertip-half',
7
+ '[&>div[data-radix-combobox-viewport]]:':
8
+ 'max-h-[var(--radix-popper-available-height)] [scrollbar-width:auto]',
9
+ '[&>div[data-radix-combobox-viewport]::-webkit-scrollbar]:': 'block',
10
+ // 'data-[side=top]:': 'animate-slide-down-and-fade',
11
+ // 'data-[side=right]:': 'animate-slide-left-and-fade',
12
+ // 'data-[side=bottom]:': 'animate-slide-up-and-fade',
13
+ // 'data-[side=left]:': 'animate-slide-right-and-fade',
14
+ }),
15
+ 'select-scroll-btn': 'flex items-center justify-center h-fingertip cursor-default',
16
+ 'select-grp-label': scFromObject({
17
+ '': 'px-$m h-fingertip flex items-center ',
18
+ 'group-data-[design=round]:': 'px-fingertip-half',
19
+ '[&>span]:': 'text-label text-grey-400',
20
+ }),
21
+ 'select-item': scFromObject({
22
+ '': 'text-body leading-none flex items-center h-fingertip relative select-none relative',
23
+ 'data-[disabled]:': 'opacity-40 pointer-events-none',
24
+ 'data-[highlighted]:': 'outline-none bg-scope-color-500/15',
25
+ '[&>span]:': 'px-$m',
26
+ 'group-data-[design=round]:[&>span]:': 'px-fingertip-half',
27
+ '[&>span]:data-[state=checked]:': 'text-scope-color-500 fw-700!',
28
+ }),
29
+ 'select-separator': 'h-[1px] bg-grey-500/10 mx-$s',
30
+ }
@@ -0,0 +1,30 @@
1
+ export interface TSelectItem {
2
+ icon?: string
3
+ search?: string
4
+ value: string | null | undefined
5
+ label?: string
6
+ disabled?: boolean
7
+ }
8
+
9
+ export type TSelectItems<T extends TSelectItem = TSelectItem> =
10
+ | Array<T | string>
11
+ | Record<string, Array<T | string>>
12
+
13
+ export interface TSelectBaseProps<T extends TSelectItem> {
14
+ required?: boolean
15
+ disabledValues?: Array<string | null | undefined>
16
+ defaultValue?: string | null | undefined
17
+ popupClass?: string | Record<string, boolean>
18
+ popupRound?: boolean
19
+ valueClass?: string | Record<string, boolean>
20
+ iconClass?: string | Record<string, boolean>
21
+ class?: string | Record<string, boolean>
22
+ items: TSelectItems<T>
23
+ disabled?: boolean
24
+ placeholder?: string
25
+ popupPosition?: 'item-aligned' | 'popper'
26
+ side?: 'top' | 'right' | 'bottom' | 'left'
27
+ sideOffset?: number
28
+ sticky?: 'partial' | 'always'
29
+ updatePositionStrategy?: 'always' | 'optimized'
30
+ }
@@ -0,0 +1,73 @@
1
+ <script setup lang="ts">
2
+ import type { SliderRootEmits, SliderRootProps } from 'radix-vue'
3
+ import { useForwardPropsEmits } from 'radix-vue'
4
+
5
+ type TClass = string | Record<string, boolean>
6
+
7
+ const props = defineProps<
8
+ SliderRootProps & {
9
+ class?: TClass
10
+ rootClass?: TClass
11
+ trackClass?: TClass
12
+ rangeClass?: TClass
13
+ hideRange?: boolean
14
+ thumbs?: number
15
+ labels?: string[]
16
+ sliderClass?: TClass | TClass[]
17
+ label?: string
18
+ displayValue?: boolean
19
+ }
20
+ >()
21
+ const emits = defineEmits<SliderRootEmits>()
22
+
23
+ const delegatedProps = computed(() => {
24
+ const {
25
+ class: _,
26
+ rootClass,
27
+ hideRange,
28
+ trackClass,
29
+ rangeClass,
30
+ thumbs,
31
+ labels,
32
+ sliderClass,
33
+ label,
34
+ displayValue,
35
+ ...delegated
36
+ } = props
37
+
38
+ return delegated
39
+ })
40
+
41
+ const forwarded = useForwardPropsEmits(delegatedProps, emits)
42
+ const modelValue = defineModel<number[]>()
43
+ function getSliderClass(n: number) {
44
+ if (Array.isArray(props.sliderClass)) {
45
+ return props.sliderClass[n]
46
+ } else if (props.sliderClass !== undefined) {
47
+ return props.sliderClass
48
+ }
49
+ return undefined
50
+ }
51
+ </script>
52
+
53
+ <template>
54
+ <div :class>
55
+ <div class="flex justify-between" v-if="!!label || displayValue">
56
+ <VuLabel>{{ label || '' }}</VuLabel>
57
+ <span class="text-label text-grey-400">{{ modelValue?.join(' | ') }}</span>
58
+ </div>
59
+ <SliderRoot v-bind="forwarded" v-model="modelValue" class="slider" :class="rootClass">
60
+ <SliderTrack :class="trackClass || 'slider-track'">
61
+ <SliderRange v-if="!hideRange" :class="rangeClass || 'slider-range'" />
62
+ </SliderTrack>
63
+
64
+ <slot>
65
+ <SliderThumb
66
+ v-for="n in thumbs || 1"
67
+ :class="getSliderClass(n) || 'slider-thumb'"
68
+ :aria-label="labels?.[n]"
69
+ />
70
+ </slot>
71
+ </SliderRoot>
72
+ </div>
73
+ </template>
@@ -0,0 +1 @@
1
+ export { default as VuSlider } from './Slider.vue'
@@ -0,0 +1,23 @@
1
+ import { scFromObject } from '../../theme/utils/shortcut-obj'
2
+
3
+ export const sliderShortcuts = {
4
+ 'slider': scFromObject({
5
+ '': 'relative flex items-center select-none touch-none min-w-2em min-h-2em',
6
+ }),
7
+ 'slider-track': scFromObject({
8
+ '': 'bg-grey-500/20 relative grow rounded-full h-[0.25em]',
9
+ }),
10
+ 'slider-range': scFromObject({
11
+ '': 'absolute bg-scope-color-500 rounded-full h-full',
12
+ }),
13
+ 'slider-thumb': scFromObject({
14
+ '': 'block w-[1.5em] h-[1.5em] bg-scope-color-500 rounded-full border-scope-light-0 border-[3px] outline-scope-color-500/10 outline-0px outline-solid',
15
+ 'dark:': 'border-scope-dark-0',
16
+ 'not-[[disabled]]:': scFromObject({
17
+ '': 'cursor-grab',
18
+ 'hover:': 'shadow-md',
19
+ 'active:': 'cursor-grabbing',
20
+ 'focus:': 'outline-[0.5em]',
21
+ }),
22
+ }),
23
+ }
@@ -0,0 +1,21 @@
1
+ import { buttonShortcuts } from './Button/shortcuts'
2
+ import { cardShortcuts } from './Card/shortcuts'
3
+ import { checkboxShortcuts } from './Checkbox/shortcuts'
4
+ import { comboboxShortcuts } from './Combobox/shortcuts'
5
+ import { loadingShortcuts } from './Loading/shortcuts'
6
+ import { menuShortcuts } from './Menu/shortcuts'
7
+ import { radioShortcuts } from './RadioGroup/shortcuts'
8
+ import { selectShortcuts } from './Select/shortcuts'
9
+ import { sliderShortcuts } from './Slider/shortcuts'
10
+
11
+ export const shortcuts = [
12
+ cardShortcuts,
13
+ menuShortcuts,
14
+ buttonShortcuts,
15
+ checkboxShortcuts,
16
+ radioShortcuts,
17
+ selectShortcuts,
18
+ comboboxShortcuts,
19
+ sliderShortcuts,
20
+ loadingShortcuts,
21
+ ]