@revojs/ui 0.2.8 → 0.2.10

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 (47) hide show
  1. package/dist/components/button.d.ts +3 -2
  2. package/dist/components/container.d.ts +2 -0
  3. package/dist/components/icon.d.ts +3 -1
  4. package/dist/components/input.d.ts +3 -2
  5. package/dist/components/navigate.d.ts +3 -2
  6. package/dist/components/separator.d.ts +1 -6
  7. package/dist/components/stack.d.ts +12 -0
  8. package/dist/components/stack.js +1 -0
  9. package/dist/index.d.ts +1 -0
  10. package/dist/module/icons.d.ts +2 -0
  11. package/dist/module/icons.js +12 -0
  12. package/dist/module/index.d.ts +1 -1
  13. package/dist/module/index.js +13 -23
  14. package/dist/style.css +7 -0
  15. package/dist/types/index.d.ts +0 -7
  16. package/dist/vue/components/breadcrumb.vue +14 -8
  17. package/dist/vue/components/button.vue +24 -19
  18. package/dist/vue/components/card.vue +21 -24
  19. package/dist/vue/components/checkbox.vue +11 -16
  20. package/dist/vue/components/collection.vue +15 -15
  21. package/dist/vue/components/container.vue +5 -6
  22. package/dist/vue/components/counter.vue +11 -14
  23. package/dist/vue/components/dialog.vue +11 -19
  24. package/dist/vue/components/field.vue +7 -16
  25. package/dist/vue/components/heading.vue +7 -8
  26. package/dist/vue/components/icon.vue +8 -42
  27. package/dist/vue/components/input.vue +18 -22
  28. package/dist/vue/components/link.vue +8 -9
  29. package/dist/vue/components/navigate.vue +2 -13
  30. package/dist/vue/components/popover.vue +57 -67
  31. package/dist/vue/components/progress.vue +9 -15
  32. package/dist/vue/components/radio.vue +9 -24
  33. package/dist/vue/components/range.vue +9 -21
  34. package/dist/vue/components/rating.vue +10 -20
  35. package/dist/vue/components/select.vue +86 -110
  36. package/dist/vue/components/separator.vue +2 -10
  37. package/dist/vue/components/sheet.vue +14 -19
  38. package/dist/vue/components/stack.vue +70 -0
  39. package/dist/vue/components/switch.vue +10 -21
  40. package/dist/vue/components/upload.vue +11 -23
  41. package/dist/vue/index.d.ts +3 -1
  42. package/dist/vue/index.js +5 -2
  43. package/dist/vue/utilities/use-focus-trap.d.ts +2 -0
  44. package/dist/vue/utilities/use-focus-trap.js +34 -0
  45. package/dist/vue/utilities/use-scroll-lock.d.ts +2 -0
  46. package/dist/vue/utilities/use-scroll-lock.js +33 -0
  47. package/package.json +5 -4
@@ -1,3 +1,4 @@
1
+ import type { IconComponent } from "./icon";
1
2
  export type ButtonType = "button" | "submit";
2
3
  export type ButtonSize = "default" | "icon";
3
4
  export type ButtonFont = "normal" | "medium" | "semibold";
@@ -5,8 +6,8 @@ export type ButtonLayout = "left" | "center" | "right";
5
6
  export type ButtonText = "sm" | "md";
6
7
  export type ButtonVariant = "primary" | "secondary" | "outline" | "ghost" | "destructive";
7
8
  export interface ButtonProps {
8
- iconPrefix?: string;
9
- iconSuffix?: string;
9
+ iconPrefix?: IconComponent;
10
+ iconSuffix?: IconComponent;
10
11
  label?: string;
11
12
  font?: ButtonFont;
12
13
  layout?: ButtonLayout;
@@ -1,4 +1,6 @@
1
+ import type { StackGap } from "./stack";
1
2
  export type ContainerSize = "3xl" | "5xl" | "7xl";
2
3
  export interface ContainerProps {
3
4
  size?: ContainerSize;
5
+ padding?: StackGap;
4
6
  }
@@ -1,3 +1,5 @@
1
+ export type IconComponent = object | ((...args: Array<never>) => unknown);
1
2
  export interface IconProps {
2
- name: string;
3
+ is: IconComponent;
4
+ size?: number;
3
5
  }
@@ -1,8 +1,9 @@
1
+ import type { IconComponent } from "./icon";
1
2
  export interface InputProps {
2
3
  modelValue?: string | number;
3
4
  type?: string;
4
- iconPrefix?: string;
5
- iconSuffix?: string;
5
+ iconPrefix?: IconComponent;
6
+ iconSuffix?: IconComponent;
6
7
  placeholder?: string;
7
8
  name?: string;
8
9
  spellcheck?: boolean;
@@ -1,7 +1,8 @@
1
+ import type { IconComponent } from "./icon";
1
2
  export interface NavigateProps {
2
3
  href: string;
3
4
  label?: string;
4
- iconPrefix?: string;
5
- iconSuffix?: string;
5
+ iconPrefix?: IconComponent;
6
+ iconSuffix?: IconComponent;
6
7
  active?: boolean;
7
8
  }
@@ -1,11 +1,6 @@
1
1
  export type SeparatorOrientation = "horizontal" | "vertical";
2
- export type DividerVariant = SeparatorOrientation;
3
2
  export interface SeparatorProps {
4
3
  orientation?: SeparatorOrientation;
5
- variant?: DividerVariant;
6
- decorative?: boolean;
7
- }
8
- export interface DividerProps {
9
- variant?: DividerVariant;
4
+ variant?: SeparatorOrientation;
10
5
  decorative?: boolean;
11
6
  }
@@ -0,0 +1,12 @@
1
+ export type StackDirection = "vertical" | "horizontal";
2
+ export type StackGap = "0" | "1" | "3" | "5" | "7";
3
+ export type StackAlign = "start" | "center" | "end" | "stretch";
4
+ export type StackJustify = "start" | "center" | "end" | "between";
5
+ export interface StackProps {
6
+ direction?: StackDirection;
7
+ gap?: StackGap;
8
+ padding?: StackGap;
9
+ align?: StackAlign;
10
+ justify?: StackJustify;
11
+ wrap?: boolean;
12
+ }
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.d.ts CHANGED
@@ -21,5 +21,6 @@ export type * from "./components/rating";
21
21
  export type * from "./components/select";
22
22
  export type * from "./components/separator";
23
23
  export type * from "./components/sheet";
24
+ export type * from "./components/stack";
24
25
  export type * from "./components/switch";
25
26
  export type * from "./components/upload";
@@ -0,0 +1,2 @@
1
+ declare const _default: import("revojs/module").Module<"ui-icons", Record<string, never>, []>;
2
+ export default _default;
@@ -0,0 +1,12 @@
1
+ import { addTypes, defineModule } from "revojs/module";
2
+ import icons from "unplugin-icons/vite";
3
+ //#region src/module/icons.ts
4
+ var icons_default = defineModule({
5
+ name: "ui-icons",
6
+ setup(config) {
7
+ addTypes(config, "ui-icons", () => "import \"unplugin-icons/types/vue3\";");
8
+ return { vite: { plugins: [icons({ compiler: "vue3" })] } };
9
+ }
10
+ });
11
+ //#endregion
12
+ export { icons_default as default };
@@ -31,5 +31,5 @@ export interface UiConfig {
31
31
  light: UiTheme;
32
32
  dark: UiTheme;
33
33
  }
34
- declare const _default: import("revojs/module").Module<"ui", UiConfig, readonly [import("revojs/module").Module<"client", import("@revojs/client/module").ClientConfig, []>]>;
34
+ declare const _default: import("revojs/module").Module<"ui", UiConfig, readonly [import("revojs/module").Module<"client", import("@revojs/client/module").ClientConfig, []>, import("revojs/module").Module<"ui-icons", Record<string, never>, []>]>;
35
35
  export default _default;
@@ -1,7 +1,7 @@
1
+ import icons_default from "./icons.js";
2
+ import { defineModule } from "revojs/module";
1
3
  import client from "@revojs/client/module";
2
4
  import tailwindcss from "@tailwindcss/vite";
3
- import { readFileSync } from "fs";
4
- import { addTypes, addVirtual, defineModule } from "revojs/module";
5
5
  import colors from "tailwindcss/colors";
6
6
  //#region src/module/index.ts
7
7
  function themeVariables(theme) {
@@ -26,9 +26,9 @@ var radii = {
26
26
  };
27
27
  var module_default = defineModule({
28
28
  name: "ui",
29
- modules: [client],
29
+ modules: [client, icons_default],
30
30
  config: () => ({
31
- radius: "rounded-md",
31
+ radius: "rounded-none",
32
32
  light: {
33
33
  background: "white",
34
34
  foreground: "zinc-950",
@@ -36,15 +36,15 @@ var module_default = defineModule({
36
36
  cardForeground: "zinc-950",
37
37
  popover: "white",
38
38
  popoverForeground: "zinc-950",
39
- primary: "green-600",
40
- primaryForeground: "zinc-50",
39
+ primary: "zinc-950",
40
+ primaryForeground: "white",
41
41
  secondary: "zinc-100",
42
42
  secondaryForeground: "zinc-900",
43
43
  muted: "zinc-100",
44
44
  mutedForeground: "zinc-500",
45
45
  accent: "zinc-100",
46
46
  accentForeground: "zinc-900",
47
- destructive: "red-600",
47
+ destructive: "red-500",
48
48
  destructiveForeground: "zinc-50",
49
49
  border: "zinc-200",
50
50
  input: "zinc-200",
@@ -53,36 +53,26 @@ var module_default = defineModule({
53
53
  dark: {
54
54
  background: "zinc-950",
55
55
  foreground: "zinc-50",
56
- card: "zinc-900",
56
+ card: "zinc-950",
57
57
  cardForeground: "zinc-50",
58
- popover: "zinc-900",
58
+ popover: "zinc-950",
59
59
  popoverForeground: "zinc-50",
60
- primary: "green-500",
60
+ primary: "white",
61
61
  primaryForeground: "zinc-950",
62
62
  secondary: "zinc-900",
63
63
  secondaryForeground: "zinc-50",
64
- muted: "zinc-800",
64
+ muted: "zinc-900",
65
65
  mutedForeground: "zinc-400",
66
66
  accent: "zinc-900",
67
67
  accentForeground: "zinc-50",
68
- destructive: "red-600",
68
+ destructive: "red-500",
69
69
  destructiveForeground: "zinc-50",
70
70
  border: "zinc-700",
71
71
  input: "zinc-700",
72
- ring: "zinc-500"
72
+ ring: "zinc-700"
73
73
  }
74
74
  }),
75
75
  setup(config) {
76
- const manifest = JSON.parse(readFileSync("package.json", "utf8"));
77
- const iconPackages = [...new Set(Object.keys({
78
- ...manifest.dependencies,
79
- ...manifest.devDependencies,
80
- ...manifest.optionalDependencies
81
- }).filter((name) => name.startsWith("@iconify-json/")))].sort();
82
- addVirtual(config, "ui/icons", () => {
83
- return `export default {${iconPackages.map((name) => `${JSON.stringify(name.slice(14))}: () => import(${JSON.stringify(`${name}/icons.json`)}).then((module) => module.default)`).join(",")}};`;
84
- });
85
- addTypes(config, "ui-icons", () => "declare module \"#ui/icons\" { import type { IconifyJSON } from \"@iconify/types\"; const icons: Record<string, () => Promise<IconifyJSON>>; export default icons; }");
86
76
  return {
87
77
  client: { html: { head: { children: [{
88
78
  tagName: "script",
package/dist/style.css CHANGED
@@ -25,6 +25,13 @@
25
25
  --radius-ui: var(--ui-radius);
26
26
  }
27
27
 
28
+ @theme {
29
+ --shadow-sheet-top: 0 25px 50px -12px rgb(0 0 0 / 0.25);
30
+ --shadow-sheet-right: -25px 0 50px -12px rgb(0 0 0 / 0.25);
31
+ --shadow-sheet-bottom: 0 -25px 50px -12px rgb(0 0 0 / 0.25);
32
+ --shadow-sheet-left: 25px 0 50px -12px rgb(0 0 0 / 0.25);
33
+ }
34
+
28
35
  @layer base {
29
36
  * {
30
37
  @apply border-border outline-ring;
@@ -5,13 +5,6 @@ declare module "#config/ui" {
5
5
  export default config;
6
6
  }
7
7
 
8
- declare module "#ui/icons" {
9
- import type { IconifyJSON } from "@iconify/types";
10
-
11
- const icons: Record<string, () => Promise<IconifyJSON>>;
12
- export default icons;
13
- }
14
-
15
8
  declare module "*.vue" {
16
9
  import type { DefineComponent } from "vue";
17
10
 
@@ -1,20 +1,26 @@
1
1
  <template>
2
2
  <nav aria-label="Breadcrumb">
3
- <ol class="flex flex-wrap items-center gap-2 text-sm">
4
- <li v-for="([label, href], index) in routes" :key="href" class="flex items-center gap-2">
3
+ <Stack is="ol" direction="horizontal" align="center" gap="3" wrap class="text-sm">
4
+ <Stack
5
+ is="li"
6
+ v-for="([label, href], index) in routes"
7
+ :key="href"
8
+ direction="horizontal"
9
+ align="center"
10
+ gap="3"
11
+ >
5
12
  <span v-if="index" aria-hidden="true" class="opacity-40"> / </span>
6
13
 
7
14
  <Link :href="href" :label="label" :variant="index === routes.length - 1 ? 'primary' : 'muted'" />
8
- </li>
9
- </ol>
15
+ </Stack>
16
+ </Stack>
10
17
  </nav>
11
18
  </template>
12
19
 
13
20
  <script setup lang="ts">
14
- import type { BreadcrumbRoute } from "../../components/breadcrumb.ts";
21
+ import type { BreadcrumbProps } from "../../components/breadcrumb";
15
22
  import Link from "./link.vue";
23
+ import Stack from "./stack.vue";
16
24
 
17
- defineProps<{
18
- routes: Array<BreadcrumbRoute>;
19
- }>();
25
+ defineProps<BreadcrumbProps>();
20
26
  </script>
@@ -1,49 +1,49 @@
1
1
  <template>
2
2
  <component
3
3
  :is="is"
4
- class="inline-flex cursor-pointer items-center justify-center gap-2 rounded-ui border font-[inherit] font-medium leading-normal no-underline outline-ring transition-[background-color,border-color,color,opacity] duration-150 focus-visible:outline-2 focus-visible:outline-offset-2 disabled:cursor-not-allowed disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50"
4
+ class="inline-flex cursor-pointer items-center justify-center gap-1 rounded-ui border font-[inherit] font-medium leading-normal no-underline outline-ring transition-[background-color,border-color,color,opacity] duration-150 select-none focus-visible:outline-2 focus-visible:outline-offset-2 disabled:cursor-not-allowed disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50"
5
5
  :class="[fonts[font], layouts[layout], sizes[size], texts[text], variants[variant]]"
6
6
  :type="attributes.type ?? 'button'"
7
7
  :disabled="disabled"
8
8
  >
9
- <Icon v-if="iconPrefix" :name="iconPrefix" />
9
+ <Icon v-if="iconPrefix" :is="iconPrefix" :size="iconSize[text]" />
10
10
 
11
11
  <slot>
12
12
  <span v-if="label" class="truncate px-1" :class="labelLayouts[layout]">{{ label }}</span>
13
13
  </slot>
14
14
 
15
- <Icon v-if="iconSuffix" :name="iconSuffix" />
15
+ <Icon v-if="iconSuffix" :is="iconSuffix" :size="iconSize[text]" />
16
16
  </component>
17
17
  </template>
18
18
 
19
19
  <script setup lang="ts">
20
20
  import { useAttrs, type Component } from "vue";
21
- import type { ButtonFont, ButtonLayout, ButtonSize, ButtonText, ButtonVariant } from "../../components/button.ts";
21
+ import type {
22
+ ButtonFont,
23
+ ButtonLayout,
24
+ ButtonProps,
25
+ ButtonSize,
26
+ ButtonText,
27
+ ButtonVariant,
28
+ } from "../../components/button";
22
29
  import Icon from "./icon.vue";
23
30
 
24
31
  const {
25
32
  is = "button",
26
- iconPrefix = undefined,
27
- iconSuffix = undefined,
28
- label = undefined,
33
+ iconPrefix,
34
+ iconSuffix,
35
+ label,
29
36
  font = "medium",
30
37
  layout = "center",
31
38
  size = "default",
32
39
  text = "sm",
33
40
  variant = "primary",
34
41
  disabled = false,
35
- } = defineProps<{
36
- is?: string | Component;
37
- iconPrefix?: string;
38
- iconSuffix?: string;
39
- label?: string;
40
- font?: ButtonFont;
41
- layout?: ButtonLayout;
42
- size?: ButtonSize;
43
- text?: ButtonText;
44
- variant?: ButtonVariant;
45
- disabled?: boolean;
46
- }>();
42
+ } = defineProps<
43
+ Omit<ButtonProps, "type"> & {
44
+ is?: string | Component;
45
+ }
46
+ >();
47
47
 
48
48
  const attributes = useAttrs();
49
49
 
@@ -75,6 +75,11 @@ const texts: Record<ButtonText, string> = {
75
75
  md: "text-base",
76
76
  };
77
77
 
78
+ const iconSize: Record<ButtonText, number> = {
79
+ sm: 20,
80
+ md: 24,
81
+ };
82
+
78
83
  const variants: Record<ButtonVariant, string> = {
79
84
  primary: "border-transparent bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/80",
80
85
  secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/90 active:bg-secondary/80",
@@ -1,44 +1,41 @@
1
1
  <template>
2
- <section
3
- class="flex flex-col overflow-hidden rounded-ui border border-border bg-card text-card-foreground shadow-sm"
4
- >
5
- <header
2
+ <Stack is="section" gap="0" class="rounded-ui border border-border bg-card text-card-foreground shadow-sm">
3
+ <Stack
6
4
  v-if="label || title || description || $slots.header || $slots.actions"
7
- class="flex items-start justify-between gap-4 border-b border-border p-5"
5
+ is="header"
6
+ direction="horizontal"
7
+ align="start"
8
+ justify="between"
9
+ class="border-b border-border p-5"
8
10
  >
9
11
  <slot name="header">
10
- <div class="flex flex-col gap-1.5">
12
+ <Stack>
11
13
  <p v-if="label" class="text-xs font-semibold uppercase tracking-widest opacity-50">{{ label }}</p>
12
14
 
13
15
  <h3 v-if="title" class="text-lg font-semibold leading-none">{{ title }}</h3>
14
16
 
15
17
  <p v-if="description" class="text-sm opacity-70">{{ description }}</p>
16
- </div>
18
+ </Stack>
17
19
  </slot>
18
20
 
19
- <div v-if="$slots.actions" class="flex shrink-0 items-center gap-1">
21
+ <Stack v-if="$slots.actions" direction="horizontal" align="center" class="shrink-0">
20
22
  <slot name="actions" />
21
- </div>
22
- </header>
23
+ </Stack>
24
+ </Stack>
23
25
 
24
- <div class="flex flex-1 flex-col gap-4 p-5">
26
+ <Stack gap="5" padding="5" class="flex-1">
25
27
  <slot />
26
- </div>
28
+ </Stack>
27
29
 
28
- <footer v-if="$slots.footer" class="flex items-center gap-3 px-5 pb-5">
30
+ <Stack v-if="$slots.footer" is="footer" direction="horizontal" align="center" gap="3" class="px-5 pb-5">
29
31
  <slot name="footer" />
30
- </footer>
31
- </section>
32
+ </Stack>
33
+ </Stack>
32
34
  </template>
33
35
 
34
36
  <script setup lang="ts">
35
- const {
36
- label = undefined,
37
- title = undefined,
38
- description = undefined,
39
- } = defineProps<{
40
- label?: string;
41
- title?: string;
42
- description?: string;
43
- }>();
37
+ import type { CardProps } from "../../components/card";
38
+ import Stack from "./stack.vue";
39
+
40
+ const { label, title, description } = defineProps<CardProps>();
44
41
  </script>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <label class="inline-flex items-start gap-3" :class="interactionVariants[interaction]">
2
+ <Stack is="label" direction="horizontal" align="start" gap="3" :class="interactionVariants[interaction]">
3
3
  <input
4
4
  class="peer sr-only"
5
5
  type="checkbox"
@@ -12,7 +12,7 @@
12
12
 
13
13
  <span
14
14
  aria-hidden="true"
15
- class="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-md border transition-colors peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-ring"
15
+ class="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-ui border transition-colors peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-ring"
16
16
  :class="modelValue ? 'border-transparent bg-primary' : 'border-input bg-background'"
17
17
  >
18
18
  <span
@@ -21,34 +21,29 @@
21
21
  />
22
22
  </span>
23
23
 
24
- <span v-if="label || description || $slots.default" class="flex flex-col gap-0.5">
24
+ <Stack is="span" v-if="label || description || $slots.default" gap="0">
25
25
  <span class="text-sm font-medium"
26
26
  ><slot>{{ label }}</slot></span
27
27
  >
28
28
  <span v-if="description" class="text-sm opacity-60">{{ description }}</span>
29
- </span>
30
- </label>
29
+ </Stack>
30
+ </Stack>
31
31
  </template>
32
32
 
33
33
  <script setup lang="ts">
34
34
  import { computed } from "vue";
35
+ import type { CheckboxProps } from "../../components/checkbox";
35
36
  import type { ControlInteraction } from "../../components/control.ts";
37
+ import Stack from "./stack.vue";
36
38
 
37
39
  const {
38
40
  modelValue = false,
39
- label = undefined,
40
- description = undefined,
41
- name = undefined,
41
+ label,
42
+ description,
43
+ name,
42
44
  required = false,
43
45
  disabled = false,
44
- } = defineProps<{
45
- modelValue?: boolean;
46
- label?: string;
47
- description?: string;
48
- name?: string;
49
- required?: boolean;
50
- disabled?: boolean;
51
- }>();
46
+ } = defineProps<CheckboxProps>();
52
47
 
53
48
  defineEmits<{
54
49
  "update:modelValue": [value: boolean];
@@ -1,32 +1,32 @@
1
1
  <template>
2
- <div
2
+ <Stack
3
3
  v-if="items"
4
- class="flex w-full flex-col divide-y divide-border overflow-hidden rounded-ui border border-border bg-card"
4
+ gap="0"
5
+ class="w-full divide-y divide-border overflow-hidden rounded-ui border border-border bg-card"
5
6
  >
6
7
  <template v-if="items.length">
7
8
  <div v-for="(item, index) in items" :key="index">
8
9
  <slot :item="item" :index="index" />
9
10
  </div>
10
11
  </template>
11
- <div v-else class="flex flex-col items-center p-5 text-sm opacity-60">{{ emptyLabel }}</div>
12
- </div>
13
- <div v-else class="grid gap-4" :class="columnsClass[columns]">
12
+ <Stack v-else align="center" padding="5" class="text-sm opacity-60">
13
+ <Icon :is="SearchIcon" class="text-2xl" />
14
+
15
+ <span>{{ emptyLabel }}</span>
16
+ </Stack>
17
+ </Stack>
18
+ <div v-else class="grid gap-5" :class="columnsClass[columns]">
14
19
  <slot />
15
20
  </div>
16
21
  </template>
17
22
 
18
23
  <script setup generic="T" lang="ts">
19
- import type { CollectionColumns } from "../../components/collection.ts";
24
+ import SearchIcon from "~icons/hugeicons/search-01";
25
+ import type { CollectionColumns, CollectionProps } from "../../components/collection";
26
+ import Icon from "./icon.vue";
27
+ import Stack from "./stack.vue";
20
28
 
21
- const {
22
- items = undefined,
23
- columns = 1,
24
- emptyLabel = "Nothing found",
25
- } = defineProps<{
26
- items?: Array<T>;
27
- columns?: CollectionColumns;
28
- emptyLabel?: string;
29
- }>();
29
+ const { items, columns = 1, emptyLabel = "Nothing found" } = defineProps<CollectionProps<T>>();
30
30
 
31
31
  defineSlots<{
32
32
  default: (() => unknown) & ((props: { item: T; index: number }) => unknown);
@@ -1,15 +1,14 @@
1
1
  <template>
2
- <div class="mx-auto flex w-full flex-col gap-5 p-5" :class="sizes[size]">
2
+ <Stack gap="5" :padding="padding" :class="sizes[size]" class="mx-auto w-full">
3
3
  <slot />
4
- </div>
4
+ </Stack>
5
5
  </template>
6
6
 
7
7
  <script setup lang="ts">
8
- import type { ContainerSize } from "../../components/container.ts";
8
+ import type { ContainerProps, ContainerSize } from "../../components/container";
9
+ import Stack from "./stack.vue";
9
10
 
10
- const { size = "5xl" } = defineProps<{
11
- size?: ContainerSize;
12
- }>();
11
+ const { size = "5xl", padding = "5" } = defineProps<ContainerProps>();
13
12
 
14
13
  const sizes: Record<ContainerSize, string> = {
15
14
  "3xl": "max-w-3xl",
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="inline-flex flex-col gap-2">
2
+ <Stack>
3
3
  <span v-if="label" class="text-sm font-medium">{{ label }}</span>
4
4
 
5
5
  <div
@@ -9,45 +9,42 @@
9
9
  class="text-lg"
10
10
  size="icon"
11
11
  variant="ghost"
12
- icon-prefix="minus-sign"
12
+ :icon-prefix="MinusIcon"
13
13
  :disabled="disabled || modelValue <= min"
14
14
  aria-label="Decrease"
15
15
  @click="change(-step)"
16
16
  />
17
17
 
18
- <output class="px-2 text-center tabular-nums">{{ modelValue }}</output>
18
+ <output class="px-1 text-center tabular-nums">{{ modelValue }}</output>
19
19
 
20
20
  <Button
21
21
  class="text-lg"
22
22
  size="icon"
23
23
  variant="ghost"
24
- icon-prefix="add-01"
24
+ :icon-prefix="AddIcon"
25
25
  :disabled="disabled || modelValue >= max"
26
26
  aria-label="Increase"
27
27
  @click="change(step)"
28
28
  />
29
29
  </div>
30
- </div>
30
+ </Stack>
31
31
  </template>
32
32
 
33
33
  <script setup lang="ts">
34
+ import AddIcon from "~icons/hugeicons/add-01";
35
+ import MinusIcon from "~icons/hugeicons/minus-sign";
36
+ import type { CounterProps } from "../../components/counter";
34
37
  import Button from "./button.vue";
38
+ import Stack from "./stack.vue";
35
39
 
36
40
  const {
37
41
  modelValue = 0,
38
- label = undefined,
42
+ label,
39
43
  min = -Infinity,
40
44
  max = Infinity,
41
45
  step = 1,
42
46
  disabled = false,
43
- } = defineProps<{
44
- modelValue?: number;
45
- label?: string;
46
- min?: number;
47
- max?: number;
48
- step?: number;
49
- disabled?: boolean;
50
- }>();
47
+ } = defineProps<CounterProps>();
51
48
 
52
49
  const emit = defineEmits<{
53
50
  "update:modelValue": [value: number];