@byyuurin/ui 0.0.5 → 0.0.7

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 (160) hide show
  1. package/README.md +15 -11
  2. package/dist/module.cjs +5 -0
  3. package/dist/module.json +12 -0
  4. package/dist/{nuxt.mjs → module.mjs} +7 -6
  5. package/dist/module.mjs.map +1 -0
  6. package/dist/runtime/components/Accordion.vue +20 -31
  7. package/dist/runtime/components/Alert.vue +1 -1
  8. package/dist/runtime/components/App.vue +1 -1
  9. package/dist/runtime/components/Badge.vue +0 -1
  10. package/dist/runtime/components/Button.vue +14 -12
  11. package/dist/runtime/components/ButtonGroup.vue +47 -0
  12. package/dist/runtime/components/Card.vue +9 -6
  13. package/dist/runtime/components/Carousel.vue +310 -0
  14. package/dist/runtime/components/Checkbox.vue +3 -4
  15. package/dist/runtime/components/Chip.vue +9 -4
  16. package/dist/runtime/components/Collapsible.vue +56 -0
  17. package/dist/runtime/components/Drawer.vue +4 -2
  18. package/dist/runtime/components/Input.vue +12 -8
  19. package/dist/runtime/components/InputNumber.vue +167 -0
  20. package/dist/runtime/components/Link.vue +301 -72
  21. package/dist/runtime/components/LinkBase.vue +88 -0
  22. package/dist/runtime/components/Modal.vue +2 -4
  23. package/dist/runtime/components/Pagination.vue +167 -0
  24. package/dist/runtime/components/PinInput.vue +0 -1
  25. package/dist/runtime/components/RadioGroup.vue +0 -1
  26. package/dist/runtime/components/ScrollArea.vue +1 -1
  27. package/dist/runtime/components/Select.vue +9 -5
  28. package/dist/runtime/components/Separator.vue +63 -0
  29. package/dist/runtime/components/Slider.vue +0 -1
  30. package/dist/runtime/components/Switch.vue +4 -6
  31. package/dist/runtime/components/Table.vue +292 -0
  32. package/dist/runtime/components/Tabs.vue +17 -18
  33. package/dist/runtime/components/Textarea.vue +0 -1
  34. package/dist/runtime/components/Toast.vue +21 -10
  35. package/dist/runtime/components/Toaster.vue +4 -39
  36. package/dist/runtime/composables/useButtonGroup.d.ts +13 -0
  37. package/dist/runtime/composables/useButtonGroup.js +14 -0
  38. package/dist/runtime/composables/{useComponentIcons.mjs → useComponentIcons.js} +1 -1
  39. package/dist/runtime/composables/useModal.d.ts +1 -1
  40. package/dist/runtime/composables/{useModal.mjs → useModal.js} +1 -1
  41. package/dist/runtime/composables/useTheme.d.ts +4 -4
  42. package/dist/runtime/composables/{useTheme.mjs → useTheme.js} +4 -4
  43. package/dist/runtime/composables/useToast.d.ts +4 -4
  44. package/dist/runtime/composables/{useToast.mjs → useToast.js} +19 -6
  45. package/dist/runtime/index.d.ts +34 -0
  46. package/dist/runtime/index.js +34 -0
  47. package/dist/runtime/theme/accordion.d.ts +22 -5
  48. package/dist/runtime/theme/{accordion.mjs → accordion.js} +2 -2
  49. package/dist/runtime/theme/alert.d.ts +41 -1
  50. package/dist/runtime/theme/{alert.mjs → alert.js} +4 -4
  51. package/dist/runtime/theme/app.d.ts +8 -0
  52. package/dist/runtime/theme/app.js +18 -0
  53. package/dist/runtime/theme/badge.d.ts +48 -21
  54. package/dist/runtime/theme/{badge.mjs → badge.js} +5 -2
  55. package/dist/runtime/theme/button-group.d.ts +66 -0
  56. package/dist/runtime/theme/button-group.js +42 -0
  57. package/dist/runtime/theme/button.d.ts +68 -111
  58. package/dist/runtime/theme/button.js +164 -0
  59. package/dist/runtime/theme/card.d.ts +38 -19
  60. package/dist/runtime/theme/card.js +37 -0
  61. package/dist/runtime/theme/carousel.d.ts +113 -0
  62. package/dist/runtime/theme/carousel.js +43 -0
  63. package/dist/runtime/theme/checkbox.d.ts +4 -1
  64. package/dist/runtime/theme/{checkbox.mjs → checkbox.js} +7 -4
  65. package/dist/runtime/theme/chip.d.ts +56 -12
  66. package/dist/runtime/theme/{chip.mjs → chip.js} +10 -7
  67. package/dist/runtime/theme/collapsible.d.ts +38 -0
  68. package/dist/runtime/theme/collapsible.js +10 -0
  69. package/dist/runtime/theme/drawer.d.ts +71 -33
  70. package/dist/runtime/theme/{drawer.mjs → drawer.js} +33 -22
  71. package/dist/runtime/theme/index.d.ts +31 -24
  72. package/dist/runtime/theme/index.js +31 -0
  73. package/dist/runtime/theme/input-number.d.ts +135 -0
  74. package/dist/runtime/theme/input-number.js +92 -0
  75. package/dist/runtime/theme/input.d.ts +94 -111
  76. package/dist/runtime/theme/input.js +151 -0
  77. package/dist/runtime/theme/link.d.ts +14 -1
  78. package/dist/runtime/theme/{link.mjs → link.js} +1 -1
  79. package/dist/runtime/theme/modal.d.ts +33 -7
  80. package/dist/runtime/theme/{modal.mjs → modal.js} +8 -10
  81. package/dist/runtime/theme/pagination.d.ts +56 -0
  82. package/dist/runtime/theme/pagination.js +13 -0
  83. package/dist/runtime/theme/pinInput.d.ts +45 -42
  84. package/dist/runtime/theme/{pinInput.mjs → pinInput.js} +14 -11
  85. package/dist/runtime/theme/popover.d.ts +16 -5
  86. package/dist/runtime/theme/{radioGroup.d.ts → radio-group.d.ts} +4 -1
  87. package/dist/runtime/theme/{radioGroup.mjs → radio-group.js} +3 -0
  88. package/dist/runtime/theme/scroll-area.d.ts +73 -0
  89. package/dist/runtime/theme/{scrollArea.mjs → scroll-area.js} +2 -2
  90. package/dist/runtime/theme/select.d.ts +95 -99
  91. package/dist/runtime/theme/{select.mjs → select.js} +22 -17
  92. package/dist/runtime/theme/separator.d.ts +95 -0
  93. package/dist/runtime/theme/separator.js +53 -0
  94. package/dist/runtime/theme/slider.d.ts +4 -1
  95. package/dist/runtime/theme/{slider.mjs → slider.js} +6 -3
  96. package/dist/runtime/theme/switch.d.ts +4 -1
  97. package/dist/runtime/theme/{switch.mjs → switch.js} +5 -2
  98. package/dist/runtime/theme/table.d.ts +89 -0
  99. package/dist/runtime/theme/table.js +35 -0
  100. package/dist/runtime/theme/tabs.d.ts +72 -52
  101. package/dist/runtime/theme/{tabs.mjs → tabs.js} +15 -12
  102. package/dist/runtime/theme/textarea.d.ts +46 -37
  103. package/dist/runtime/theme/{textarea.mjs → textarea.js} +14 -11
  104. package/dist/runtime/theme/toast.d.ts +45 -7
  105. package/dist/runtime/theme/{toast.mjs → toast.js} +12 -7
  106. package/dist/runtime/theme/toaster.d.ts +89 -25
  107. package/dist/runtime/theme/{toaster.mjs → toaster.js} +5 -0
  108. package/dist/runtime/theme/tooltip.d.ts +20 -7
  109. package/dist/runtime/theme/{tooltip.mjs → tooltip.js} +2 -2
  110. package/dist/runtime/types/components.d.ts +7 -1
  111. package/dist/runtime/types/index.d.ts +4 -4
  112. package/dist/runtime/types/index.js +2 -0
  113. package/dist/runtime/types/utils.d.ts +1 -1
  114. package/dist/runtime/utils/index.d.ts +3 -3
  115. package/dist/runtime/utils/{index.mjs → index.js} +3 -3
  116. package/dist/runtime/utils/link.d.ts +22 -7
  117. package/dist/runtime/utils/link.js +30 -0
  118. package/dist/runtime/utils/styler.d.ts +2 -2
  119. package/dist/runtime/vue/stubs.d.ts +9 -0
  120. package/dist/runtime/vue/stubs.js +16 -0
  121. package/dist/shared/ui.d1728164.mjs +4 -0
  122. package/dist/shared/ui.d1728164.mjs.map +1 -0
  123. package/dist/types.d.mts +1 -0
  124. package/dist/types.d.ts +1 -0
  125. package/dist/{unocss-preset.d.ts → unocss.d.mts} +10 -15
  126. package/dist/{unocss-preset.d.mts → unocss.d.ts} +10 -15
  127. package/dist/{unocss-preset.mjs → unocss.mjs} +53 -21
  128. package/dist/unocss.mjs.map +1 -0
  129. package/dist/unplugin.d.mts +4 -3
  130. package/dist/unplugin.d.ts +4 -3
  131. package/dist/unplugin.mjs +49 -5
  132. package/dist/unplugin.mjs.map +1 -1
  133. package/dist/vite.d.mts +1 -1
  134. package/dist/vite.d.ts +1 -1
  135. package/dist/vite.mjs +7 -5
  136. package/dist/vite.mjs.map +1 -1
  137. package/package.json +59 -45
  138. package/dist/index.d.ts +0 -26
  139. package/dist/index.mjs +0 -26
  140. package/dist/nuxt.mjs.map +0 -1
  141. package/dist/runtime/theme/app.mjs +0 -10
  142. package/dist/runtime/theme/button.mjs +0 -143
  143. package/dist/runtime/theme/card.mjs +0 -14
  144. package/dist/runtime/theme/index.mjs +0 -24
  145. package/dist/runtime/theme/input.mjs +0 -146
  146. package/dist/runtime/theme/scrollArea.d.ts +0 -51
  147. package/dist/runtime/types/index.mjs +0 -2
  148. package/dist/runtime/utils/link.mjs +0 -4
  149. package/dist/shared/ui.CzDyI29e.mjs +0 -8
  150. package/dist/shared/ui.CzDyI29e.mjs.map +0 -1
  151. package/dist/unocss-preset.mjs.map +0 -1
  152. /package/{LICENSE.md → LICENSE} +0 -0
  153. /package/dist/{nuxt.d.mts → module.d.mts} +0 -0
  154. /package/dist/{nuxt.d.ts → module.d.ts} +0 -0
  155. /package/dist/runtime/composables/{defineInjection.mjs → defineInjection.js} +0 -0
  156. /package/dist/runtime/theme/{popover.mjs → popover.js} +0 -0
  157. /package/dist/runtime/types/{components.mjs → components.js} +0 -0
  158. /package/dist/runtime/types/{utils.mjs → utils.js} +0 -0
  159. /package/dist/runtime/utils/{extend-theme.mjs → extend-theme.js} +0 -0
  160. /package/dist/runtime/utils/{styler.mjs → styler.js} +0 -0
@@ -1,117 +1,346 @@
1
1
  <script lang="ts">
2
2
  import type { PrimitiveProps } from 'reka-ui'
3
+ import type { ButtonHTMLAttributes } from 'vue'
4
+ import type { RouteLocationRaw, RouterLinkProps } from 'vue-router'
3
5
  import type { link } from '../theme'
4
- import type { ComponentAttrs, HintString, MaybeArray } from '../types'
6
+ import type { ComponentAttrs } from '../types'
5
7
 
6
- export interface LinkProps extends Omit<ComponentAttrs<typeof link>, 'ui'> {
8
+ interface NuxtLinkProps extends Omit<RouterLinkProps, 'to'> {
9
+ /**
10
+ * Route Location the link should navigate to when clicked on.
11
+ */
12
+ to?: RouteLocationRaw // need to manually type to avoid breaking typedPages
13
+ /**
14
+ * An alias for `to`. If used with `to`, `href` will be ignored
15
+ */
16
+ href?: NuxtLinkProps['to']
17
+ /**
18
+ * Forces the link to be considered as external (true) or internal (false). This is helpful to handle edge-cases
19
+ */
20
+ external?: boolean
21
+ /**
22
+ * Where to display the linked URL, as the name for a browsing context.
23
+ */
24
+ target?: '_blank' | '_parent' | '_self' | '_top' | (string & {}) | null
25
+ /**
26
+ * A rel attribute value to apply on the link. Defaults to "noopener noreferrer" for external links.
27
+ */
28
+ rel?: 'noopener' | 'noreferrer' | 'nofollow' | 'sponsored' | 'ugc' | (string & {}) | null
29
+ /**
30
+ * If set to true, no rel attribute will be added to the link
31
+ */
32
+ noRel?: boolean
33
+ /**
34
+ * A class to apply to links that have been prefetched.
35
+ */
36
+ prefetchedClass?: string
37
+ /**
38
+ * When enabled will prefetch middleware, layouts and payloads of links in the viewport.
39
+ */
40
+ prefetch?: boolean
41
+ /**
42
+ * Allows controlling when to prefetch links. By default, prefetch is triggered only on visibility.
43
+ */
44
+ prefetchOn?: 'visibility' | 'interaction' | Partial<{
45
+ visibility: boolean
46
+ interaction: boolean
47
+ }>
48
+ /**
49
+ * Escape hatch to disable `prefetch` attribute.
50
+ */
51
+ noPrefetch?: boolean
52
+ }
53
+
54
+ export interface LinkProps extends Omit<ComponentAttrs<typeof link>, 'ui'>, NuxtLinkProps {
55
+ /**
56
+ * The element or component this component should render as when not a link.
57
+ * @default 'button'
58
+ */
7
59
  as?: PrimitiveProps['as']
8
- type?: string
9
- onClick?: MaybeArray<(e: MouseEvent) => void | Promise<void>>
60
+ /**
61
+ * The type of the button when not a link.
62
+ * @default 'button'
63
+ */
64
+ type?: ButtonHTMLAttributes['type']
10
65
  label?: string
11
- href?: string
12
- navigate?: (e: MouseEvent) => void
13
- /** A rel attribute value to apply on the link. */
14
- rel?: HintString<'noopener' | 'noreferrer' | 'nofollow' | 'sponsored' | 'ugc'> | null
15
- noRel?: boolean
16
- /** Where to display the linked URL, as the name for a browsing context. */
17
- target?: HintString<'_blank' | '_parent' | '_self' | '_top'> | null
18
- isExternal?: boolean
19
- underline?: boolean
20
- active?: boolean
21
66
  disabled?: boolean
67
+ /** Force the link to be active independent of the current route. */
68
+ active?: boolean
69
+ /** Will only be active if the current route is an exact match. */
70
+ exact?: boolean
71
+ /** Will only be active if the current route query is an exact match. */
72
+ exactQuery?: boolean | 'partial'
73
+ /** Will only be active if the current route hash is an exact match. */
74
+ exactHash?: boolean
75
+ /** The class to apply when the link is inactive. */
76
+ inactiveClass?: string
77
+ /** The class to apply when the link is disabled. */
78
+ disableClass?: string
79
+ custom?: boolean
80
+ underline?: boolean
22
81
  /** When `true`, only styles from `class`, `ui.active`, and `ui.inactive` will be applied. */
23
82
  raw?: boolean
24
- ui?: {
25
- active?: string
26
- inactive?: string
27
- disabled?: string
28
- }
83
+ }
84
+
85
+ export interface LinkSlots {
86
+ default?: (props: { active: boolean }) => any
29
87
  }
30
88
  </script>
31
89
 
32
90
  <script setup lang="ts">
33
- import { Primitive } from 'reka-ui'
34
- import { computed } from 'vue'
91
+ import { reactiveOmit } from '@vueuse/core'
92
+ import { diff, isEqual } from 'ohash/utils'
93
+ import { useForwardProps } from 'reka-ui'
94
+ import { hasProtocol } from 'ufo'
95
+ import { computed, getCurrentInstance, resolveComponent } from 'vue'
96
+ import { useNuxtApp, useRoute } from '#imports'
35
97
  import { useTheme } from '../composables/useTheme'
98
+ import LinkBase from './LinkBase.vue'
99
+
100
+ defineOptions({ inheritAttrs: false })
36
101
 
37
102
  const props = withDefaults(defineProps<LinkProps>(), {
38
103
  as: 'button',
39
104
  type: 'button',
40
105
  })
106
+ defineSlots<LinkSlots>()
107
+
108
+ // Check if vue-router is available by checking for the injection key
109
+ const hasRouter = computed(() => {
110
+ const app = getCurrentInstance()?.appContext.app
111
+ return !!(app?.config?.globalProperties?.$router)
112
+ })
113
+
114
+ const nuxtApp = useNuxtApp()
115
+ const hasNuxtLink = !!nuxtApp.$router
116
+
117
+ const linkComponent = computed(() => ({
118
+ NuxtLink: nuxtApp.$router ? resolveComponent('NuxtLink') : null,
119
+ RouterLink: hasRouter.value ? resolveComponent('RouterLink') : null,
120
+ }))
121
+
122
+ // Only try to get route if router exists
123
+ const route = computed(() => {
124
+ if (!hasRouter.value)
125
+ return null
41
126
 
42
- const linkProps = computed(() => {
43
- const { as, type, disabled, href, rel, target } = props
44
- const base = { as, rel, target }
45
-
46
- if (href) {
47
- return {
48
- ...base,
49
- 'as': 'a',
50
- 'href': disabled ? undefined : href,
51
- 'aria-disabled': disabled ? 'true' : undefined,
52
- 'role': disabled ? 'link' : undefined,
53
- 'tabindex': disabled ? -1 : undefined,
54
- }
127
+ try {
128
+ return useRoute()
55
129
  }
130
+ catch {
131
+ return null
132
+ }
133
+ })
134
+
135
+ const linkProps = useForwardProps(reactiveOmit(
136
+ props,
137
+ 'as',
138
+ 'type',
139
+ 'disabled',
140
+ 'active',
141
+ 'exact',
142
+ 'exactQuery',
143
+ 'exactHash',
144
+ 'activeClass',
145
+ 'inactiveClass',
146
+ 'raw',
147
+ 'class',
148
+ ...(hasNuxtLink ? [] : ['to'] as const),
149
+ ))
150
+
151
+ function isPartiallyEqual(item1: any, item2: any) {
152
+ const diffedKeys = diff(item1, item2).reduce((filtered, q) => {
153
+ if (q.type === 'added')
154
+ filtered.add(q.key)
155
+
156
+ return filtered
157
+ }, new Set<string>())
56
158
 
57
- if (as === 'button')
58
- return { ...base, type, disabled }
159
+ const item1Filtered = Object.fromEntries(Object.entries(item1).filter(([key]) => !diffedKeys.has(key)))
160
+ const item2Filtered = Object.fromEntries(Object.entries(item2).filter(([key]) => !diffedKeys.has(key)))
59
161
 
60
- return base
162
+ return isEqual(item1Filtered, item2Filtered)
163
+ }
164
+
165
+ const isExternalLink = computed(() => {
166
+ if (!props.to)
167
+ return false
168
+
169
+ return typeof props.to === 'string' && hasProtocol(props.to, { acceptRelative: true })
61
170
  })
62
171
 
63
- const { theme, createStyler } = useTheme()
172
+ function isLinkActive({ route: linkRoute, isActive, isExactActive }: any) {
173
+ if (props.active !== undefined)
174
+ return props.active
64
175
 
65
- const style = computed(() => {
66
- if (props.raw)
67
- return props.class
176
+ if (props.exactQuery === 'partial') {
177
+ if (!isPartiallyEqual(linkRoute?.query, route.value?.query))
178
+ return false
179
+ }
180
+ else if (props.exactQuery === true && !isEqual(linkRoute?.query, route.value?.query)) {
181
+ return false
182
+ }
183
+
184
+ if (props.exactHash && linkRoute?.hash !== route.value?.hash)
185
+ return false
186
+
187
+ if (props.exact && isExactActive)
188
+ return true
68
189
 
190
+ if (!props.exact && isActive)
191
+ return true
192
+
193
+ return false
194
+ }
195
+
196
+ const { theme, createStyler } = useTheme()
197
+
198
+ function resolveLinkClass({ route, isActive, isExactActive }: any = {}) {
69
199
  const { link } = theme.value
200
+ const active = isLinkActive({ route, isActive, isExactActive })
201
+
202
+ if (props.raw)
203
+ return [props.class, active ? props.activeClass : props.inactiveClass]
70
204
 
71
205
  const styler = createStyler({
72
206
  ...link,
73
207
  variants: {
74
208
  ...link.variants,
75
209
  active: {
76
- true: [link.variants.active.true, props.ui?.active],
77
- false: [link.variants.active.false, props.ui?.inactive],
210
+ true: [link.variants.active.true, props.activeClass],
211
+ false: [link.variants.active.false, props.inactiveClass],
78
212
  },
79
213
  disabled: {
80
- true: [link.variants.disabled.true, props.ui?.disabled],
214
+ true: [link.variants.disabled.true, props.disableClass],
81
215
  },
82
216
  },
83
217
  })
84
- return styler(props)
85
- })
86
-
87
- function handleClick(e: MouseEvent) {
88
- if (props.disabled) {
89
- e.stopPropagation()
90
- e.preventDefault()
91
- return
92
- }
93
-
94
- if (props.onClick) {
95
- const handlers = Array.isArray(props.onClick) ? props.onClick : [props.onClick]
96
- for (const handler of handlers)
97
- handler(e)
98
- }
99
218
 
100
- if (props.href && props.navigate && !props.isExternal)
101
- props.navigate(e)
219
+ return styler({ ...props, active })
102
220
  }
103
221
  </script>
104
222
 
105
223
  <template>
106
- <Primitive
107
- v-bind="linkProps"
108
- :rel="props.rel"
109
- :target="props.target"
110
- :class="style"
111
- @click="handleClick"
112
- >
113
- <slot :active="props.active">
114
- {{ props.label }}
115
- </slot>
116
- </Primitive>
224
+ <template v-if="hasRouter">
225
+ <component
226
+ :is="linkComponent.NuxtLink"
227
+ v-if="hasNuxtLink"
228
+ v-slot="{ href, navigate, route: linkRoute, rel, target, isExternal, isActive, isExactActive }"
229
+ v-bind="linkProps"
230
+ :href="to ? undefined : href"
231
+ custom
232
+ >
233
+ <template v-if="custom">
234
+ <slot
235
+ v-bind="{
236
+ ...$attrs,
237
+ as,
238
+ type,
239
+ disabled,
240
+ href,
241
+ navigate,
242
+ rel,
243
+ target,
244
+ isExternal,
245
+ active: isLinkActive({ route: linkRoute, isActive, isExactActive }),
246
+ }"
247
+ >
248
+ {{ props.label }}
249
+ </slot>
250
+ </template>
251
+ <LinkBase
252
+ v-else
253
+ v-bind="{
254
+ ...$attrs,
255
+ as,
256
+ type,
257
+ disabled,
258
+ href,
259
+ navigate,
260
+ rel,
261
+ target,
262
+ isExternal,
263
+ }"
264
+ :class="resolveLinkClass({ route: linkRoute, isActive, isExactActive })"
265
+ >
266
+ <slot :active="isLinkActive({ route: linkRoute, isActive, isExactActive })">
267
+ {{ props.label }}
268
+ </slot>
269
+ </LinkBase>
270
+ </component>
271
+ <component
272
+ :is="linkComponent.RouterLink"
273
+ v-else
274
+ v-slot="{ href, navigate, route: linkRoute, isActive, isExactActive }"
275
+ v-bind="linkProps"
276
+ :to="to || '#'"
277
+ custom
278
+ >
279
+ <template v-if="custom">
280
+ <slot
281
+ v-bind="{
282
+ ...$attrs,
283
+ as,
284
+ type,
285
+ disabled,
286
+ href: to ? href : undefined,
287
+ navigate,
288
+ active: isLinkActive({ route: linkRoute, isActive, isExactActive }),
289
+ }"
290
+ >
291
+ {{ props.label }}
292
+ </slot>
293
+ </template>
294
+ <LinkBase
295
+ v-else
296
+ v-bind="{
297
+ ...$attrs,
298
+ as,
299
+ type,
300
+ disabled,
301
+ href: to ? href : undefined,
302
+ navigate,
303
+ }"
304
+ :class="resolveLinkClass({ route: linkRoute, isActive, isExactActive })"
305
+ >
306
+ <slot :active="isLinkActive({ route: linkRoute, isActive, isExactActive })">
307
+ {{ props.label }}
308
+ </slot>
309
+ </LinkBase>
310
+ </component>
311
+ </template>
312
+ <template v-else>
313
+ <template v-if="props.custom">
314
+ <slot
315
+ v-bind="{
316
+ ...$attrs,
317
+ as,
318
+ type,
319
+ disabled,
320
+ href: to,
321
+ target: isExternalLink ? '_blank' : undefined,
322
+ active: false,
323
+ }"
324
+ >
325
+ {{ props.label }}
326
+ </slot>
327
+ </template>
328
+ <LinkBase
329
+ v-else
330
+ v-bind="{
331
+ ...$attrs,
332
+ as,
333
+ type,
334
+ disabled,
335
+ href: (to as string),
336
+ target: isExternalLink ? '_blank' : undefined,
337
+ }"
338
+ :is-external="isExternalLink"
339
+ :class="resolveLinkClass()"
340
+ >
341
+ <slot :active="false">
342
+ {{ props.label }}
343
+ </slot>
344
+ </LinkBase>
345
+ </template>
117
346
  </template>
@@ -0,0 +1,88 @@
1
+ <script lang="ts">
2
+ import type { PrimitiveProps } from 'reka-ui'
3
+
4
+ export interface LinkBaseProps {
5
+ /**
6
+ * The element or component this component should render as.
7
+ * @default 'button'
8
+ */
9
+ as?: PrimitiveProps['as']
10
+ type?: string
11
+ disabled?: boolean
12
+ onClick?: ((e: MouseEvent) => void | Promise<void>) | Array<((e: MouseEvent) => void | Promise<void>)>
13
+ href?: string
14
+ navigate?: (e: MouseEvent) => void
15
+ rel?: string
16
+ target?: string
17
+ isExternal?: boolean
18
+ }
19
+ </script>
20
+
21
+ <script setup lang="ts">
22
+ import { Primitive } from 'reka-ui'
23
+ import { computed } from 'vue'
24
+
25
+ const props = withDefaults(defineProps<LinkBaseProps>(), {
26
+ as: 'button',
27
+ type: 'button',
28
+ })
29
+
30
+ const wrapperProps = computed(() => {
31
+ const { href, rel, target, disabled } = props
32
+
33
+ const baseProps = {
34
+ rel,
35
+ target,
36
+ }
37
+
38
+ if (props.href) {
39
+ return {
40
+ ...baseProps,
41
+ 'as': 'a',
42
+ 'href': disabled ? undefined : href,
43
+ 'aria-disabled': disabled ? 'true' : undefined,
44
+ 'role': disabled ? 'link' : undefined,
45
+ 'tabindex': disabled ? -1 : undefined,
46
+ }
47
+ }
48
+
49
+ if (props.as === 'button') {
50
+ return {
51
+ ...baseProps,
52
+ as: props.as,
53
+ type: props.type,
54
+ disabled,
55
+ }
56
+ }
57
+
58
+ return {
59
+ ...baseProps,
60
+ as: props.as,
61
+ }
62
+ })
63
+
64
+ function onClickWrapper(e: MouseEvent) {
65
+ if (props.disabled) {
66
+ e.stopPropagation()
67
+ e.preventDefault()
68
+ return
69
+ }
70
+
71
+ if (props.onClick) {
72
+ for (const onClick of Array.isArray(props.onClick) ? props.onClick : [props.onClick])
73
+ onClick(e)
74
+ }
75
+
76
+ if (props.href && props.navigate && !props.isExternal)
77
+ props.navigate(e)
78
+ }
79
+ </script>
80
+
81
+ <template>
82
+ <Primitive
83
+ v-bind="wrapperProps"
84
+ @click="onClickWrapper"
85
+ >
86
+ <slot></slot>
87
+ </Primitive>
88
+ </template>
@@ -28,7 +28,6 @@ export interface ModalProps extends ComponentAttrs<typeof modal>, DialogRootProp
28
28
  portal?: boolean
29
29
  /** @default true */
30
30
  overlay?: boolean
31
- blur?: boolean
32
31
  /** @default true */
33
32
  transition?: boolean
34
33
  /**
@@ -47,11 +46,10 @@ import { reactivePick } from '@vueuse/core'
47
46
  import { DialogClose, DialogContent, DialogDescription, DialogOverlay, DialogPortal, DialogRoot, DialogTitle, DialogTrigger, useForwardPropsEmits } from 'reka-ui'
48
47
  import { computed, toRef } from 'vue'
49
48
  import { useTheme } from '../composables/useTheme'
50
- import UButton from './Button.vue'
49
+ import Button from './Button.vue'
51
50
 
52
51
  const props = withDefaults(defineProps<ModalProps>(), {
53
52
  modal: true,
54
- size: 'md',
55
53
  portal: true,
56
54
  overlay: true,
57
55
  transition: true,
@@ -110,7 +108,7 @@ const style = computed(() => {
110
108
 
111
109
  <DialogClose as-child>
112
110
  <slot name="close">
113
- <UButton
111
+ <Button
114
112
  v-if="props.close"
115
113
  variant="ghost"
116
114
  :icon="props.closeIcon || theme.app.icons.close"
@@ -0,0 +1,167 @@
1
+ <script lang="ts">
2
+ import type { PaginationRootEmits, PaginationRootProps } from 'reka-ui'
3
+ import type { pagination } from '../theme'
4
+ import type { ButtonProps, ComponentAttrs } from '../types'
5
+
6
+ export interface PaginationProps extends ComponentAttrs<typeof pagination>, Pick<PaginationRootProps, 'as' | 'defaultPage' | 'disabled' | 'itemsPerPage' | 'page' | 'showEdges' | 'siblingCount' | 'total'> {
7
+ /**
8
+ * The icon to use for the first page control.
9
+ * @default app.icons.doubleLeft
10
+ */
11
+ firstIcon?: string
12
+ /**
13
+ * The icon to use for the last page control.
14
+ * @default app.icons.doubleRight
15
+ */
16
+ lastIcon?: string
17
+ /**
18
+ * The icon to use for the previous page control.
19
+ * @default app.icons.arrowLeft
20
+ */
21
+ prevIcon?: string
22
+ /**
23
+ * The icon to use for the next page control.
24
+ * @default app.icons.arrowRight
25
+ */
26
+ nextIcon?: string
27
+ /**
28
+ * The icon to use for the ellipsis control.
29
+ * @default app.icons.ellipsis
30
+ */
31
+ ellipsisIcon?: string
32
+ /**
33
+ * The size of the pagination controls.
34
+ * @default 'md'
35
+ */
36
+ size?: ButtonProps['size']
37
+ /**
38
+ * The variant of the pagination controls.
39
+ * @default 'outline'
40
+ */
41
+ variant?: ButtonProps['variant']
42
+ /**
43
+ * The variant of the active pagination control.
44
+ * @default 'solid'
45
+ */
46
+ activeVariant?: ButtonProps['variant']
47
+ /**
48
+ * Whether to show the first, previous, next, and last controls.
49
+ * @default true
50
+ */
51
+ showControls?: boolean
52
+ }
53
+
54
+ export interface PaginationEmits extends PaginationRootEmits {}
55
+
56
+ export interface PaginationSlots {
57
+ first?: (props?: {}) => any
58
+ prev?: (props?: {}) => any
59
+ next?: (props?: {}) => any
60
+ last?: (props?: {}) => any
61
+ ellipsis?: (props?: {}) => any
62
+ item?: (props: {
63
+ page: number
64
+ pageCount: number
65
+ item: {
66
+ type: 'ellipsis'
67
+ } | {
68
+ type: 'page'
69
+ value: number
70
+ }
71
+ index: number
72
+ }) => any
73
+ }
74
+ </script>
75
+
76
+ <script setup lang="ts">
77
+ import { reactivePick } from '@vueuse/core'
78
+ import { PaginationEllipsis, PaginationFirst, PaginationLast, PaginationList, PaginationListItem, PaginationNext, PaginationPrev, PaginationRoot, useForwardPropsEmits } from 'reka-ui'
79
+ import { computed } from 'vue'
80
+ import { useTheme } from '../composables/useTheme'
81
+ import Button from './Button.vue'
82
+
83
+ const props = withDefaults(defineProps<PaginationProps>(), {
84
+ variant: 'outline',
85
+ activeVariant: 'solid',
86
+ showControls: true,
87
+ showEdges: false,
88
+ itemsPerPage: 10,
89
+ siblingCount: 2,
90
+ total: 0,
91
+ })
92
+
93
+ const emit = defineEmits<PaginationEmits>()
94
+ const slots = defineSlots<PaginationSlots>()
95
+
96
+ const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'defaultPage', 'disabled', 'itemsPerPage', 'page', 'showEdges', 'siblingCount', 'total'), emit)
97
+
98
+ const { theme, createStyler } = useTheme()
99
+
100
+ const firstIcon = computed(() => props.firstIcon || theme.value.app.icons.doubleLeft)
101
+ const prevIcon = computed(() => props.prevIcon || theme.value.app.icons.arrowLeft)
102
+ const nextIcon = computed(() => props.nextIcon || theme.value.app.icons.arrowRight)
103
+ const lastIcon = computed(() => props.lastIcon || theme.value.app.icons.doubleRight)
104
+ const ellipsisIcon = computed(() => props.ellipsisIcon || theme.value.app.icons.ellipsis)
105
+
106
+ const style = computed(() => {
107
+ const styler = createStyler(theme.value.pagination)
108
+ return styler(props)
109
+ })
110
+ </script>
111
+
112
+ <template>
113
+ <PaginationRoot v-slot="{ page, pageCount }" v-bind="rootProps" :class="style.root({ class: [props.class, props.ui?.root] })">
114
+ <PaginationList v-slot="{ items }" :class="style.list({ class: props.ui?.list })">
115
+ <PaginationFirst v-if="props.showControls || !!slots.first" as-child>
116
+ <slot name="first">
117
+ <Button :class="style.item({ class: props.ui?.item })" :variant="props.variant" :size="props.size" :icon="firstIcon" />
118
+ </slot>
119
+ </PaginationFirst>
120
+ <PaginationPrev v-if="props.showControls || !!slots.prev" as-child>
121
+ <slot name="prev">
122
+ <Button :class="style.item({ class: props.ui?.item })" :variant="props.variant" :size="props.size" :icon="prevIcon" />
123
+ </slot>
124
+ </PaginationPrev>
125
+
126
+ <template v-for="(item, index) in items">
127
+ <PaginationListItem v-if="item.type === 'page'" :key="index" as-child :value="item.value">
128
+ <slot name="item" v-bind="{ item, index, page, pageCount }">
129
+ <Button
130
+ :class="style.item({ class: props.ui?.item })"
131
+ :variant="props.page === item.value ? props.activeVariant : props.variant"
132
+ :size="props.size"
133
+ :label="String(item.value)"
134
+ :ui="{ label: style.label() }"
135
+ />
136
+ </slot>
137
+ </PaginationListItem>
138
+
139
+ <PaginationEllipsis v-else :key="item.type" :index="index" as-child>
140
+ <slot name="ellipsis">
141
+ <Button
142
+ :variant="props.variant"
143
+ :size="props.size"
144
+ :icon="ellipsisIcon"
145
+ :disabled="props.disabled /* TODO: remove after reka-ui update */"
146
+ :class="[
147
+ style.item({ class: props.ui?.item }),
148
+ style.ellipsis({ class: props.ui?.ellipsis }),
149
+ ]"
150
+ />
151
+ </slot>
152
+ </PaginationEllipsis>
153
+ </template>
154
+
155
+ <PaginationNext v-if="props.showControls || !!slots.next" as-child>
156
+ <slot name="next">
157
+ <Button :class="style.item({ class: props.ui?.item })" :variant="props.variant" :size="props.size" :icon="nextIcon" />
158
+ </slot>
159
+ </PaginationNext>
160
+ <PaginationLast v-if="props.showControls || !!slots.last" as-child>
161
+ <slot name="last">
162
+ <Button :class="style.item({ class: props.ui?.item })" :variant="props.variant" :size="props.size" :icon="lastIcon" />
163
+ </slot>
164
+ </PaginationLast>
165
+ </PaginationList>
166
+ </PaginationRoot>
167
+ </template>