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
package/dist/vite.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { ComponentResolver } from 'unplugin-vue-components';
2
+
3
+ export declare const VunorVueResolver: ComponentResolver;
4
+
5
+ export { }
package/dist/vite.mjs ADDED
@@ -0,0 +1,13 @@
1
+ const u = (r) => {
2
+ if (r.startsWith("Vu")) {
3
+ const s = r.slice(2);
4
+ return {
5
+ name: "default",
6
+ as: r,
7
+ from: `vunor/${s}.vue`
8
+ };
9
+ }
10
+ };
11
+ export {
12
+ u as VunorVueResolver
13
+ };
package/package.json ADDED
@@ -0,0 +1,83 @@
1
+ {
2
+ "name": "vunor",
3
+ "version": "0.0.2",
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "vite",
7
+ "build-with-checks": "run-p type-check \"build {@}\" --",
8
+ "preview": "vite preview",
9
+ "build": "vite build",
10
+ "pub": "npm publish --access public",
11
+ "type-check": "vue-tsc --build --force"
12
+ },
13
+ "dependencies": {
14
+ "@prostojs/palitra": "^0.0.3",
15
+ "@unocss/preset-mini": "^0.60.4",
16
+ "defu": "^6.1.4",
17
+ "radix-vue": "^1.8.3",
18
+ "unocss": "^0.60.4",
19
+ "vue": "^3.4.27",
20
+ "vue-router": "^4.3.2"
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "src/components"
25
+ ],
26
+ "exports": {
27
+ "./package.json": "./package.json",
28
+ "./theme": {
29
+ "import": "./dist/theme.mjs",
30
+ "types": "./dist/theme.d.ts"
31
+ },
32
+ "./vite": {
33
+ "import": "./dist/vite.mjs",
34
+ "types": "./dist/vite.d.ts"
35
+ },
36
+ "./Input.vue": "./src/components/Input/Input.vue",
37
+ "./InputBase.vue": "./src/components/Input/InputBase.vue",
38
+ "./Button.vue": "./src/components/Button/Button.vue",
39
+ "./ButtonBase.vue": "./src/components/Button/ButtonBase.vue",
40
+ "./AppLayout.vue": "./src/components/AppLayout/AppLayout.vue",
41
+ "./Card.vue": "./src/components/Card/Card.vue",
42
+ "./CardHeader.vue": "./src/components/Card/CardHeader.vue",
43
+ "./CardInner.vue": "./src/components/Card/CardInner.vue",
44
+ "./Checkbox.vue": "./src/components/Checkbox/Checkbox.vue",
45
+ "./Combobox.vue": "./src/components/Combobox/Combobox.vue",
46
+ "./Icon.vue": "./src/components/Icon/Icon.vue",
47
+ "./InputShell.vue": "./src/components/Input/InputShell.vue",
48
+ "./Label.vue": "./src/components/Label/Label.vue",
49
+ "./LoadingIndicator.vue": "./src/components/Loading/LoadingIndicator.vue",
50
+ "./Menu.vue": "./src/components/Menu/Menu.vue",
51
+ "./MenuItem.vue": "./src/components/Menu/MenuItem.vue",
52
+ "./OverflowContainer.vue": "./src/components/OverflowContainer/OverflowContainer.vue",
53
+ "./Pagination.vue": "./src/components/Pagination/Pagination.vue",
54
+ "./Popover.vue": "./src/components/Popover/Popover.vue",
55
+ "./RadioGroup.vue": "./src/components/RadioGroup/RadioGroup.vue",
56
+ "./Select.vue": "./src/components/Select/Select.vue",
57
+ "./SelectBase.vue": "./src/components/Select/SelectBase.vue",
58
+ "./Slider.vue": "./src/components/Slider/Slider.vue",
59
+ "./utils": "./src/components/utils/index.ts"
60
+ },
61
+ "devDependencies": {
62
+ "@iconify/utils": "^2.1.23",
63
+ "@prostojs/palitra": "^0.0.3",
64
+ "@tsconfig/node20": "^20.1.4",
65
+ "@types/node": "^20.12.5",
66
+ "@unocss/preset-icons": "^0.59.4",
67
+ "@unocss/preset-mini": "^0.59.4",
68
+ "@unocss/preset-wind": "^0.59.4",
69
+ "@unocss/reset": "^0.59.4",
70
+ "@vitejs/plugin-vue": "^5.0.4",
71
+ "@vue/tsconfig": "^0.5.1",
72
+ "@vueuse/core": "^10.10.0",
73
+ "typescript": "~5.4.5",
74
+ "unocss": "^0.60.4",
75
+ "unocss-preset-scrollbar": "^0.3.1",
76
+ "unplugin-auto-import": "^0.17.6",
77
+ "unplugin-vue-components": "^0.27.0",
78
+ "vite": "^5.2.8",
79
+ "vite-plugin-dts": "^3.9.1",
80
+ "vitest": "^1.6.0",
81
+ "vue-tsc": "^2.0.11"
82
+ }
83
+ }
@@ -0,0 +1,114 @@
1
+ <script setup lang="ts">
2
+ const props = withDefaults(
3
+ defineProps<{
4
+ maxW?: string
5
+ headerH?: string
6
+ footerH?: string
7
+ leftW?: string
8
+ rightW?: string
9
+ footer?: boolean
10
+ header?: boolean
11
+ left?: boolean
12
+ right?: boolean
13
+ headerClass?: string | Record<string, boolean>
14
+ footerClass?: string | Record<string, boolean>
15
+ leftClass?: string | Record<string, boolean>
16
+ rightClass?: string | Record<string, boolean>
17
+ mainClass?: string | Record<string, boolean>
18
+ scrollTopOnChangeView?: boolean
19
+ }>(),
20
+ {
21
+ maxW: '90rem',
22
+ headerH: '3rem',
23
+ footerH: '3rem',
24
+ leftW: '14rem',
25
+ rightW: '14rem',
26
+ }
27
+ )
28
+
29
+ const vars = computed(() =>
30
+ [
31
+ `--app-max-w: ${props.maxW}`,
32
+ `--app-header-h: ${props.header ? props.headerH : '0rem'}`,
33
+ `--app-footer-h: ${props.footer ? props.footerH : '0rem'}`,
34
+ `--app-left-w: ${props.left ? props.leftW : '0rem'}`,
35
+ `--app-right-w: ${props.right ? props.rightW : '0rem'}`,
36
+ ].join(';')
37
+ )
38
+
39
+ const mainScroll = ref<HTMLDivElement>()
40
+ const main = ref<HTMLDivElement>()
41
+
42
+ let observer: MutationObserver
43
+
44
+ const autoScroll: MutationCallback = (changes: MutationRecord[]) => {
45
+ if (changes.length && changes.some(c => c.type === 'childList') && mainScroll.value) {
46
+ mainScroll.value.scroll({ top: 0, left: 0 })
47
+ }
48
+ }
49
+
50
+ onMounted(() => {
51
+ if (props.scrollTopOnChangeView && main.value) {
52
+ observer = new MutationObserver(autoScroll)
53
+ observer.observe(main.value, {
54
+ childList: true,
55
+ subtree: false,
56
+ })
57
+ }
58
+ })
59
+
60
+ onBeforeUnmount(() => {
61
+ if (observer) {
62
+ observer.disconnect()
63
+ }
64
+ })
65
+ </script>
66
+
67
+ <template>
68
+ <div class="h-screen w-full flex flex-col items-center" :style="vars">
69
+ <div ref="mainScroll" class="flex-1 overflow-y-scroll overflow-x-auto w-full thin-scrollbar">
70
+ <header
71
+ v-if="header"
72
+ class="sticky top-0 flex-shrink-0 h-[var(--app-header-h)] w-full flex"
73
+ :class="headerClass"
74
+ >
75
+ <div class="mx-auto max-w-[var(--app-max-w)] flex items-center justify-between w-full">
76
+ <slot name="header"></slot>
77
+ </div>
78
+ </header>
79
+
80
+ <div class="max-w-[var(--app-max-w)] w-full mx-auto flex">
81
+ <aside
82
+ class="hidden lg:block w-[var(--app-left-w)] top-[var(--app-header-h)] overflow-auto fixed"
83
+ :style="'height: calc(100vh - var(--app-header-h) - var(--app-footer-h))'"
84
+ >
85
+ <div class="min-h-full" :class="leftClass">
86
+ <slot name="left"></slot>
87
+ </div>
88
+ </aside>
89
+
90
+ <main
91
+ class="flex-1 overflow-auto lg:pl-[var(--app-left-w)] lg:pr-[var(--app-right-w)]"
92
+ :class="mainClass"
93
+ ref="main"
94
+ >
95
+ <slot></slot>
96
+ </main>
97
+
98
+ <aside
99
+ class="hidden xl:block w-[var(--app-right-w)] top-[var(--app-header-h)] overflow-auto fixed"
100
+ :class="rightClass"
101
+ :style="'height: calc(100vh - var(--app-header-h) - var(--app-footer-h)); left: calc(50% - var(--app-right-w) + min(var(--app-max-w), 100vw) / 2);'"
102
+ >
103
+ <slot name="right"></slot>
104
+ </aside>
105
+ </div>
106
+ </div>
107
+
108
+ <footer v-if="footer" class="flex-shrink-0 h-[var(--app-footer-h)] px-4 w-full flex">
109
+ <div class="mx-auto max-w-[var(--app-max-w)] flex items-center justify-between w-full">
110
+ footer
111
+ </div>
112
+ </footer>
113
+ </div>
114
+ </template>
@@ -0,0 +1,49 @@
1
+ <script setup lang="ts">
2
+ import { RouterLink, type RouteLocationRaw } from 'vue-router'
3
+
4
+ const props = defineProps<{
5
+ label?: string
6
+ icon?: string
7
+ iconSide?: 'left' | 'right'
8
+ loading?: boolean
9
+ disabled?: boolean
10
+ active?: boolean
11
+ pressed?: boolean
12
+ selected?: boolean
13
+ to?: RouteLocationRaw
14
+ class?: string | Record<string, boolean>
15
+ style?: string | Record<string, string>
16
+ asLink?: boolean
17
+ }>()
18
+ </script>
19
+
20
+ <template>
21
+ <RouterLink v-if="!!to" custom :to v-slot="{ isActive, href }">
22
+ <VuButtonBase as="a" :href :aria-selected="isActive" v-bind="props">
23
+ <template #icon-left v-if="$slots['icon-left']">
24
+ <slot name="icon-left"></slot>
25
+ </template>
26
+ <slot></slot>
27
+ <template #icon-right v-if="$slots['icon-right']">
28
+ <slot name="icon-right"></slot>
29
+ </template>
30
+ </VuButtonBase>
31
+ </RouterLink>
32
+
33
+ <VuButtonBase
34
+ v-else
35
+ :as="asLink ? 'a' : 'button'"
36
+ v-bind="props"
37
+ :data-active="active ? '' : undefined"
38
+ :aria-pressed="typeof pressed === 'boolean' ? pressed : undefined"
39
+ :aria-selected="typeof selected === 'boolean' ? selected : undefined"
40
+ >
41
+ <template #icon-left v-if="$slots['icon-left']">
42
+ <slot name="icon-left"></slot>
43
+ </template>
44
+ <slot></slot>
45
+ <template #icon-right v-if="$slots['icon-right']">
46
+ <slot name="icon-right"></slot>
47
+ </template>
48
+ </VuButtonBase>
49
+ </template>
@@ -0,0 +1,43 @@
1
+ <script setup lang="ts">
2
+ withDefaults(
3
+ defineProps<{
4
+ label?: string
5
+ icon?: string
6
+ iconSide?: 'left' | 'right'
7
+ loading?: boolean
8
+ disabled?: boolean
9
+ as?: string
10
+ href?: string
11
+ }>(),
12
+ {
13
+ iconSide: 'left',
14
+ as: 'button',
15
+ }
16
+ )
17
+ </script>
18
+
19
+ <template>
20
+ <Primitive
21
+ class="btn group/btn"
22
+ :as
23
+ :href
24
+ :data-has-label="!!label || !!$slots.default"
25
+ :data-has-icon="!!icon"
26
+ :data-loading="loading ? '' : undefined"
27
+ :disabled="loading || disabled || undefined"
28
+ >
29
+ <div class="loading-indicator-wrapper" v-if="loading">
30
+ <VuLoadingIndicator />
31
+ </div>
32
+ <slot name="icon-left">
33
+ <VuIcon v-if="icon && iconSide === 'left'" :name="icon" class="btn-icon btn-icon-left" />
34
+ </slot>
35
+ <slot>
36
+ <span class="btn-label">{{ label }}</span>
37
+ </slot>
38
+
39
+ <slot name="icon-right">
40
+ <VuIcon v-if="icon && iconSide === 'right'" :name="icon" class="btn-icon btn-icon-right" />
41
+ </slot>
42
+ </Primitive>
43
+ </template>
@@ -0,0 +1 @@
1
+ export { default as VuButton } from './Button.vue'
@@ -0,0 +1,29 @@
1
+ import { scFromObject } from '../../theme/utils/shortcut-obj'
2
+
3
+ export const buttonShortcuts = {
4
+ 'btn': scFromObject({
5
+ '': 'h-fingertip flex items-center justify-center px-$m gap-$xs select-none fw-bold tracking-wide relative',
6
+ '[&.btn-round]:': 'px-fingertip-half rounded-fingertip-half',
7
+ '[&.btn-square]:': 'size-fingertip px-0',
8
+ '[&.btn-round.btn-square]:': 'px-0',
9
+ 'disabled:': 'opacity-80 cursor-not-allowed',
10
+ '[&>span]:data-[loading]:': 'opacity-0 pointer-events-none',
11
+ '[&>div:not(.loading-indicator-wrapper)]:data-[loading]:': 'opacity-0 pointer-events-none',
12
+ '[&>.loading-indicator-wrapper]:':
13
+ 'absolute left-0 top-0 right-0 bottom-0 flex items-center justify-center cursor-wait',
14
+ }),
15
+ 'btn-square': scFromObject({
16
+ '': '',
17
+ }),
18
+ 'btn-label': scFromObject({
19
+ '': 'lh-1em ellipsis whitespace-nowrap overflow-x-clip overflow-y-visible',
20
+ 'group-[.btn-square]/btn:': 'hidden',
21
+ }),
22
+ 'btn-icon': scFromObject({
23
+ '': 'size-1em font-size-1.25em',
24
+ 'group-[.btn-round]/btn:[&.btn-icon-left]:': 'ml-[-0.5em]',
25
+ 'group-[.btn-round]/btn:[&.btn-icon-right]:': 'mr-[-0.5em]',
26
+ 'group-[.btn-square]/btn:': 'font-size-1.5em m-0!',
27
+ 'group-[.btn-round.btn-square]/btn:': 'm-0!',
28
+ }),
29
+ }
@@ -0,0 +1,47 @@
1
+ <script setup lang="ts">
2
+ import { useCardPI } from './pi'
3
+
4
+ const props = withDefaults(
5
+ defineProps<{
6
+ as?: string
7
+ asChild?: boolean
8
+ noPadding?: boolean
9
+ level?:
10
+ | 'h1'
11
+ | 'h2'
12
+ | 'h3'
13
+ | 'h4'
14
+ | 'h5'
15
+ | 'h6'
16
+ | 'subheading'
17
+ | 'body-l'
18
+ | 'body'
19
+ | 'body-s'
20
+ | 'callout'
21
+ dense?: boolean
22
+ rounded?: boolean
23
+ }>(),
24
+ {
25
+ as: 'div',
26
+ }
27
+ )
28
+
29
+ const { headerLevel } = useCardPI().provide()
30
+
31
+ const _level = computed(() => props.level || headerLevel.value || 'h6')
32
+ </script>
33
+
34
+ <template>
35
+ <Primitive
36
+ :as
37
+ :asChild
38
+ :data-rounded="rounded"
39
+ :data-dense="dense"
40
+ :data-level="_level"
41
+ :style="noPadding ? 'padding: 0' : ''"
42
+ class="card"
43
+ >
44
+ <slot />
45
+ </Primitive>
46
+ </template>
47
+ ./pi
@@ -0,0 +1,26 @@
1
+ <script setup lang="ts">
2
+ import { useCardPI } from './pi'
3
+
4
+ const props = defineProps<{
5
+ as?: string
6
+ asChild?: boolean
7
+ level?: string
8
+ }>()
9
+
10
+ const _as = computed(() =>
11
+ props.as
12
+ ? props.as
13
+ : ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(props.level || '')
14
+ ? props.level
15
+ : 'h6'
16
+ )
17
+
18
+ useCardPI().inject()
19
+ </script>
20
+
21
+ <template>
22
+ <Primitive :as="_as" :asChild class="text-card-header text-mt-0">
23
+ <slot></slot>
24
+ </Primitive>
25
+ </template>
26
+ ./pi
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <Primitive class="p-$card-spacing">
3
+ <slot></slot>
4
+ </Primitive>
5
+ </template>
@@ -0,0 +1,3 @@
1
+ export { default as VuCard } from './Card.vue'
2
+ export { default as VuCardHeader } from './CardHeader.vue'
3
+ export { default as VuCardInner } from './CardInner.vue'
@@ -0,0 +1,46 @@
1
+ /* eslint-disable sonarjs/cognitive-complexity */
2
+ /* eslint-disable @typescript-eslint/no-unsafe-argument */
3
+ /* eslint-disable @typescript-eslint/no-unsafe-call */
4
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
5
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
6
+ /* eslint-disable @typescript-eslint/strict-boolean-expressions */
7
+
8
+ import { useProvideInject } from '../utils'
9
+
10
+ const safeTag = (t?: string) => (t && /^h[1-6]$/.test(t) ? t : '')
11
+
12
+ export const useCardPI = () =>
13
+ useProvideInject('__vunor_card_PI', () => {
14
+ const headerLevel = ref('')
15
+
16
+ function updateCardLevel(v: string) {
17
+ if (v) {
18
+ headerLevel.value = v
19
+ }
20
+ }
21
+
22
+ return {
23
+ _provide: () => ({ headerLevel }),
24
+ _inject: () => {
25
+ const instance = getCurrentInstance()
26
+ if (instance) {
27
+ const v2 = (instance.props.level || safeTag(instance.props.as as string)) as string
28
+ if (v2) {
29
+ updateCardLevel(v2)
30
+ } else {
31
+ onMounted(() => {
32
+ const v = (instance.props.level || safeTag(instance.props.as as string)) as string
33
+ const tag = instance.vnode.el?.tagName.toLowerCase()
34
+ updateCardLevel(v || safeTag(tag) ? tag : '')
35
+ })
36
+ }
37
+ watch(
38
+ () => [instance.props.level, instance.props.as],
39
+ () => {
40
+ updateCardLevel((instance.props.level || instance.props.as) as string)
41
+ }
42
+ )
43
+ }
44
+ },
45
+ }
46
+ })
@@ -0,0 +1,19 @@
1
+ const headers = [
2
+ 'h1',
3
+ 'h2',
4
+ 'h3',
5
+ 'h4',
6
+ 'h5',
7
+ 'h6',
8
+ 'subheading',
9
+ 'body-l',
10
+ 'body',
11
+ 'body-s',
12
+ 'callout',
13
+ ]
14
+
15
+ export const cardShortcuts = {
16
+ card: `data-[rounded=true]:rounded-$card-spacing data-[dense=true]:card-dense! ${headers
17
+ .map(h => `data-[level=${h}]:card-${h}`)
18
+ .join(' ')}`,
19
+ }
@@ -0,0 +1,59 @@
1
+ <script setup lang="ts">
2
+ const modelValue = defineModel<boolean | undefined | 'indeterminate'>()
3
+ defineProps<{
4
+ label?: string
5
+ disabled?: boolean
6
+ error?: string | boolean
7
+ required?: boolean
8
+ verticalMiddle?: boolean
9
+ reverse?: boolean
10
+ readonly?: boolean
11
+ }>()
12
+ </script>
13
+
14
+ <template>
15
+ <!-- [&>.checkbox]:hover:bg-neutral-100 -->
16
+ <div>
17
+ <label
18
+ :style="{
19
+ 'pointer-events': readonly ? 'none' : undefined,
20
+ }"
21
+ :aria-disabled="disabled"
22
+ class="checkbox-root group/cb"
23
+ :aria-readonly="readonly || undefined"
24
+ :data-error="!!error"
25
+ :class="{
26
+ 'items-center': verticalMiddle,
27
+ 'items-start': !verticalMiddle,
28
+ 'flex-row-reverse': reverse,
29
+ }"
30
+ >
31
+ <CheckboxRoot
32
+ :disabled
33
+ :required="true"
34
+ :aria-required="required"
35
+ v-model:checked="modelValue"
36
+ :tabindex="readonly ? '-1' : undefined"
37
+ class="checkbox"
38
+ :aria-readonly="readonly || undefined"
39
+ >
40
+ <CheckboxIndicator class="checkbox-indicator">
41
+ <VuIcon v-if="modelValue === 'indeterminate'" name="i--dash" class="checkbox-icon" />
42
+ <VuIcon v-if="modelValue === true" name="i--checkmark" class="checkbox-icon" />
43
+ </CheckboxIndicator>
44
+ </CheckboxRoot>
45
+ <span class="checkbox-label">
46
+ <slot :label="label">
47
+ {{ label }}
48
+ </slot>
49
+ </span>
50
+ </label>
51
+ <div
52
+ v-if="!!error && typeof error === 'string'"
53
+ class="text-caption text-error-500 text-mt-$s"
54
+ :class="{ 'text-right': reverse }"
55
+ >
56
+ {{ error }}
57
+ </div>
58
+ </div>
59
+ </template>
@@ -0,0 +1 @@
1
+ export { default as VuCheckbox } from './Checkbox.vue'
@@ -0,0 +1,27 @@
1
+ import { scFromObject } from '../../theme/utils/shortcut-obj'
2
+
3
+ export const checkboxShortcuts = {
4
+ 'checkbox-root': scFromObject({
5
+ '': 'text-body select-none flex gap-$m cursor-default current-bg-scope-color-500 current-border-scope-color-500',
6
+ 'data-[error=true]:': 'current-border-error-500',
7
+ 'aria-[disabled=true]:': 'scope-grey opacity-50 cursor-not-allowed',
8
+ }),
9
+ 'checkbox': scFromObject({
10
+ '': 'cursor-default shrink-0 select-none rounded-[0.28em] transition-all transition-duration-100 flex size-1.5em appearance-none items-center justify-center bg-current/0 backdrop-blur-sm border-current border-[0.16em] current-icon-white',
11
+ 'group-active/cb:enabled:':
12
+ 'current-bg-scope-color-500 bg-current/20 current-icon-scope-color-500',
13
+ // 'group-hover/cb:enabled:': 'border-current',
14
+ 'disabled:': 'cursor-not-allowed border-1px backdrop-blur-sm',
15
+ 'group-[[data-error=true]]/cb:enabled:': 'current-border-error-500 border-current',
16
+ 'data-[state=unchecked]': 'current-border-grey-500 border-current/40',
17
+ }),
18
+ 'checkbox-indicator': scFromObject({
19
+ '': 'bg-current icon-current h-full w-full flex items-center justify-center',
20
+ }),
21
+ 'checkbox-icon': scFromObject({
22
+ '': 'size-0.9em animate-cb-appear animate-duration-200 animate-ease',
23
+ }),
24
+ 'checkbox-label': scFromObject({
25
+ '': 'select-none text-body lh-1.5em',
26
+ }),
27
+ }