@boxcustodia/library 2.0.0-alpha.13 → 2.0.0-alpha.14

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 (173) hide show
  1. package/dist/index.cjs.js +1 -138
  2. package/dist/index.d.ts +1083 -715
  3. package/dist/index.es.js +7077 -56175
  4. package/dist/theme.css +1 -1
  5. package/package.json +34 -26
  6. package/src/__doc__/Examples.tsx +1 -1
  7. package/src/__doc__/Intro.mdx +3 -3
  8. package/src/__doc__/Tabs.mdx +112 -0
  9. package/src/__doc__/V2.mdx +1246 -0
  10. package/src/components/accordion/accordion.stories.tsx +143 -0
  11. package/src/components/accordion/accordion.tsx +135 -0
  12. package/src/components/accordion/index.ts +1 -0
  13. package/src/components/alert/alert.stories.tsx +24 -4
  14. package/src/components/alert/alert.tsx +17 -9
  15. package/src/components/alert-dialog/alert-dialog.stories.tsx +24 -0
  16. package/src/components/alert-dialog/alert-dialog.test.tsx +1 -1
  17. package/src/components/alert-dialog/alert-dialog.tsx +58 -10
  18. package/src/components/auto-complete/auto-complete.stories.tsx +616 -200
  19. package/src/components/auto-complete/auto-complete.tsx +420 -68
  20. package/src/components/auto-complete/index.ts +0 -1
  21. package/src/components/avatar/avatar.stories.tsx +162 -21
  22. package/src/components/avatar/avatar.tsx +79 -20
  23. package/src/components/button/button.stories.tsx +219 -294
  24. package/src/components/button/button.test.tsx +10 -17
  25. package/src/components/button/button.tsx +78 -19
  26. package/src/components/button/components/base-button.tsx +30 -53
  27. package/src/components/button/index.ts +0 -1
  28. package/src/components/calendar/calendar.stories.tsx +1 -1
  29. package/src/components/calendar/calendar.tsx +4 -4
  30. package/src/components/card/card.stories.tsx +141 -69
  31. package/src/components/card/card.tsx +155 -54
  32. package/src/components/center/center.stories.tsx +22 -39
  33. package/src/components/checkbox/checkbox.stories.tsx +25 -5
  34. package/src/components/checkbox/checkbox.tsx +76 -15
  35. package/src/components/checkbox-group/checkbox-group.stories.tsx +116 -28
  36. package/src/components/checkbox-group/checkbox-group.tsx +84 -3
  37. package/src/components/combobox/combobox.stories.tsx +33 -23
  38. package/src/components/combobox/combobox.tsx +119 -103
  39. package/src/components/date-picker/date-input.stories.tsx +14 -6
  40. package/src/components/date-picker/date-input.tsx +2 -2
  41. package/src/components/date-picker/date-picker.model.ts +13 -4
  42. package/src/components/date-picker/date-picker.stories.tsx +38 -12
  43. package/src/components/date-picker/date-picker.tsx +28 -14
  44. package/src/components/dialog/dialog.stories.tsx +18 -0
  45. package/src/components/dialog/dialog.test.tsx +1 -1
  46. package/src/components/dialog/dialog.tsx +51 -20
  47. package/src/components/divider/divider.stories.tsx +6 -0
  48. package/src/components/dropzone/dropzone.stories.tsx +71 -90
  49. package/src/components/dropzone/dropzone.tsx +383 -105
  50. package/src/components/dropzone/index.ts +0 -1
  51. package/src/components/empty/empty.stories.tsx +165 -0
  52. package/src/components/empty/empty.tsx +156 -0
  53. package/src/components/empty/index.ts +1 -0
  54. package/src/components/field/field.stories.tsx +226 -3
  55. package/src/components/field/field.tsx +77 -42
  56. package/src/components/form/form.stories.tsx +320 -197
  57. package/src/components/form/form.tsx +3 -23
  58. package/src/components/index.ts +2 -6
  59. package/src/components/input/input.stories.tsx +5 -5
  60. package/src/components/input/input.tsx +4 -4
  61. package/src/components/kbd/kbd.stories.tsx +1 -0
  62. package/src/components/label/label.stories.tsx +16 -0
  63. package/src/components/label/label.tsx +13 -2
  64. package/src/components/loader/loader.stories.tsx +7 -5
  65. package/src/components/loader/loader.tsx +8 -3
  66. package/src/components/menu/menu-primitives.tsx +207 -196
  67. package/src/components/menu/menu.stories.tsx +276 -146
  68. package/src/components/menu/menu.tsx +146 -54
  69. package/src/components/number-input/number-input.stories.tsx +27 -4
  70. package/src/components/number-input/number-input.test.tsx +2 -2
  71. package/src/components/number-input/number-input.tsx +25 -29
  72. package/src/components/otp/index.ts +1 -0
  73. package/src/components/otp/otp.stories.tsx +209 -0
  74. package/src/components/otp/otp.tsx +100 -0
  75. package/src/components/pagination/index.ts +1 -0
  76. package/src/components/pagination/pagination.model.ts +2 -0
  77. package/src/components/pagination/pagination.stories.tsx +154 -59
  78. package/src/components/pagination/pagination.test.tsx +122 -57
  79. package/src/components/pagination/pagination.tsx +575 -77
  80. package/src/components/password/password.stories.tsx +18 -3
  81. package/src/components/password/password.tsx +26 -10
  82. package/src/components/popover/popover.stories.tsx +26 -5
  83. package/src/components/popover/popover.tsx +15 -23
  84. package/src/components/progress/progress.stories.tsx +1 -0
  85. package/src/components/radio-group/index.ts +1 -0
  86. package/src/components/radio-group/radio-group.stories.tsx +251 -0
  87. package/src/components/radio-group/radio-group.tsx +212 -0
  88. package/src/components/scroll-area/scroll-area.stories.tsx +1 -0
  89. package/src/components/select/select.stories.tsx +118 -19
  90. package/src/components/select/select.tsx +67 -62
  91. package/src/components/skeleton/skeleton.stories.tsx +1 -0
  92. package/src/components/stack/stack.stories.tsx +179 -89
  93. package/src/components/stack/stack.tsx +2 -2
  94. package/src/components/stepper/index.ts +1 -1
  95. package/src/components/stepper/stepper.stories.tsx +767 -83
  96. package/src/components/stepper/stepper.test.tsx +18 -18
  97. package/src/components/stepper/stepper.tsx +554 -0
  98. package/src/components/switch/switch.stories.tsx +15 -1
  99. package/src/components/switch/switch.tsx +17 -4
  100. package/src/components/table/index.ts +0 -2
  101. package/src/components/table/table.stories.tsx +131 -18
  102. package/src/components/table/table.test.tsx +1 -1
  103. package/src/components/table/table.tsx +183 -77
  104. package/src/components/tabs/tabs.stories.tsx +373 -155
  105. package/src/components/tabs/tabs.test.tsx +12 -12
  106. package/src/components/tabs/tabs.tsx +72 -149
  107. package/src/components/tag/index.ts +0 -1
  108. package/src/components/tag/tag.stories.tsx +155 -120
  109. package/src/components/tag/tag.tsx +47 -95
  110. package/src/components/textarea/textarea.stories.tsx +8 -22
  111. package/src/components/textarea/textarea.tsx +17 -79
  112. package/src/components/timeline/timeline.stories.tsx +323 -42
  113. package/src/components/timeline/timeline.tsx +359 -132
  114. package/src/components/toast/toast.stories.tsx +1 -0
  115. package/src/components/tooltip/tooltip.tsx +11 -9
  116. package/src/components/tree/index.ts +0 -1
  117. package/src/components/tree/tree.stories.tsx +365 -408
  118. package/src/components/tree/tree.test.tsx +163 -0
  119. package/src/components/tree/tree.tsx +212 -36
  120. package/src/hooks/useAsync/__doc__/useAsync.stories.tsx +5 -5
  121. package/src/hooks/useClipboard/__doc__/useClipboard.stories.tsx +1 -3
  122. package/src/hooks/useDebounceCallback/__doc__/useDebouncedCallback.stories.tsx +6 -6
  123. package/src/hooks/useDocumentTitle/__doc__/useDocumentTitle.stories.tsx +1 -1
  124. package/src/hooks/useEventListener/__test__/useEventListener.test.tsx +1 -1
  125. package/src/hooks/useLocalStorage/__doc__/useLocalStorage.stories.tsx +1 -1
  126. package/src/hooks/usePagination/usePagination.tsx +36 -24
  127. package/src/styles/theme.css +1 -1
  128. package/src/utils/form.tsx +67 -37
  129. package/src/utils/index.ts +1 -1
  130. package/src/__doc__/Migration.mdx +0 -451
  131. package/src/components/auto-complete/auto-complete-primitives.tsx +0 -155
  132. package/src/components/background-image/background-image.stories.tsx +0 -21
  133. package/src/components/background-image/background-image.test.tsx +0 -29
  134. package/src/components/background-image/background-image.tsx +0 -23
  135. package/src/components/background-image/index.ts +0 -1
  136. package/src/components/button/button.variants.ts +0 -44
  137. package/src/components/button/components/loader-overlay.tsx +0 -21
  138. package/src/components/button/components/loading-icon.tsx +0 -47
  139. package/src/components/dropzone/upload-primitives.tsx +0 -310
  140. package/src/components/dropzone/use-dropzone.ts +0 -122
  141. package/src/components/empty-state/empty-state.stories.tsx +0 -56
  142. package/src/components/empty-state/empty-state.tsx +0 -39
  143. package/src/components/empty-state/index.ts +0 -1
  144. package/src/components/heading/heading.stories.tsx +0 -74
  145. package/src/components/heading/heading.tsx +0 -28
  146. package/src/components/heading/heading.variants.ts +0 -27
  147. package/src/components/heading/index.ts +0 -1
  148. package/src/components/kbd/kbd.variants.ts +0 -26
  149. package/src/components/menu/util/render-menu-item.tsx +0 -54
  150. package/src/components/multi-select/hooks/use-multi-select.ts +0 -66
  151. package/src/components/multi-select/index.ts +0 -1
  152. package/src/components/multi-select/multi-select.stories.tsx +0 -294
  153. package/src/components/multi-select/multi-select.tsx +0 -300
  154. package/src/components/multi-select/multi-select.variants.ts +0 -22
  155. package/src/components/pagination/components/pagination-option.tsx +0 -27
  156. package/src/components/show/index.ts +0 -1
  157. package/src/components/show/show.stories.tsx +0 -197
  158. package/src/components/show/show.test.tsx +0 -41
  159. package/src/components/show/show.tsx +0 -16
  160. package/src/components/stepper/Stepper.tsx +0 -190
  161. package/src/components/stepper/context/stepper-context.tsx +0 -11
  162. package/src/components/table/table-primitives.tsx +0 -122
  163. package/src/components/table/table.model.ts +0 -20
  164. package/src/components/table-pagination/index.ts +0 -2
  165. package/src/components/table-pagination/table-pagination.model.ts +0 -2
  166. package/src/components/table-pagination/table-pagination.stories.tsx +0 -23
  167. package/src/components/table-pagination/table-pagination.test.tsx +0 -32
  168. package/src/components/table-pagination/table-pagination.tsx +0 -108
  169. package/src/components/tabs/context/tabs-context.tsx +0 -14
  170. package/src/components/tag/tag.variants.ts +0 -31
  171. package/src/components/timeline/timeline-status.ts +0 -5
  172. package/src/components/tree/hooks/use-controllable-tree-state.ts +0 -80
  173. package/src/components/tree/tree-primitives.tsx +0 -126
@@ -1,175 +1,98 @@
1
- import { useControllableState } from "@radix-ui/react-use-controllable-state";
2
- import { HTMLProps, ReactNode, useEffect, useRef, useState } from "react";
3
- import { cn } from "../../lib";
4
- import { TabsContextProvider, useTabsContext } from "./context/tabs-context";
1
+ "use client";
5
2
 
6
- interface TabsExtendedProps extends TabsProps {
7
- listClassName: TabListProps["className"];
8
- listProps: TabListProps;
9
- triggerClassName: TabTriggerProps["className"];
10
- triggerProps: TabTriggerProps;
11
- contentClassName: TabContentProps["className"];
12
- contentProps: TabContentProps;
13
- }
3
+ import { Tabs as TabsPrimitive } from "@base-ui/react/tabs";
4
+ import type React from "react";
5
+ import { cn } from "../../lib";
14
6
 
15
- interface TabsProps
16
- extends Omit<
17
- HTMLProps<HTMLDivElement>,
18
- "value" | "defaultValue" | "onChange"
19
- > {
20
- value?: string;
21
- onChange?: (tab: string) => void;
22
- defaultValue?: string;
23
- variant?: "default" | "background";
24
- }
7
+ export type TabsVariant = "underline" | "background";
25
8
 
26
- const Tabs = ({
27
- children,
28
- value: prop,
29
- onChange,
30
- defaultValue,
31
- variant = "default",
9
+ export function Tabs({
10
+ className,
32
11
  ...props
33
- }: TabsProps) => {
34
- const [activeTab = "", setActiveTab] = useControllableState<string>({
35
- prop,
36
- onChange,
37
- defaultProp: defaultValue as string,
38
- });
39
- const [targetRef, setTargetRef] = useState<HTMLButtonElement | null>(null);
40
-
12
+ }: TabsPrimitive.Root.Props): React.ReactElement {
41
13
  return (
42
- <TabsContextProvider
43
- value={{ activeTab, setActiveTab, setTargetRef, targetRef, variant }}
44
- >
45
- <div data-slot="tabs" {...props}>
46
- {children}
47
- </div>
48
- </TabsContextProvider>
14
+ <TabsPrimitive.Root
15
+ className={cn(
16
+ "flex flex-col gap-2 data-[orientation=vertical]:flex-row",
17
+ className,
18
+ )}
19
+ data-slot="tabs"
20
+ {...props}
21
+ />
49
22
  );
50
- };
51
-
52
- interface TabListProps {
53
- children: ReactNode;
54
- className?: string;
55
- indicatorClassName?: string;
56
23
  }
57
24
 
58
- const TabList = ({ children, className, indicatorClassName }: TabListProps) => {
59
- const { targetRef } = useTabsContext();
60
-
25
+ export function TabsList({
26
+ variant = "underline",
27
+ centered = false,
28
+ className,
29
+ children,
30
+ ...props
31
+ }: TabsPrimitive.List.Props & {
32
+ variant?: TabsVariant;
33
+ centered?: boolean;
34
+ }): React.ReactElement {
61
35
  return (
62
- <div data-slot="tabs-list" className={cn("relative mb-4", className)}>
63
- <div data-slot="tabs-content" className="flex">
64
- {children}
65
- </div>
66
- <TabsIndicator
67
- targetRef={targetRef}
68
- indicatorClassName={indicatorClassName}
36
+ <TabsPrimitive.List
37
+ data-variant={variant}
38
+ className={cn(
39
+ "group/tabs-list relative z-0 flex w-fit items-center justify-center gap-x-0.5 text-muted-foreground",
40
+ centered && "mx-auto",
41
+ "data-[orientation=vertical]:flex-col data-[orientation=vertical]:justify-start",
42
+ variant === "background"
43
+ ? "rounded-lg border border-border p-0.5"
44
+ : "data-[orientation=vertical]:px-1 data-[orientation=horizontal]:py-1 *:data-[slot=tabs-tab]:hover:bg-accent",
45
+ className,
46
+ )}
47
+ data-slot="tabs-list"
48
+ {...props}
49
+ >
50
+ {children}
51
+ <TabsPrimitive.Indicator
52
+ className={cn(
53
+ "absolute bottom-0 left-0 h-(--active-tab-height) w-(--active-tab-width) translate-x-(--active-tab-left) -translate-y-(--active-tab-bottom) transition-[width,translate] duration-200 ease-in-out",
54
+ variant === "background"
55
+ ? "-z-1 rounded-md bg-primary shadow-sm/5"
56
+ : "z-10 bg-primary data-[orientation=horizontal]:h-0.5 data-[orientation=vertical]:w-0.5 data-[orientation=vertical]:-translate-x-px data-[orientation=horizontal]:translate-y-px",
57
+ )}
58
+ data-slot="tab-indicator"
69
59
  />
70
- </div>
60
+ </TabsPrimitive.List>
71
61
  );
72
- };
73
-
74
- interface TabTriggerProps
75
- extends Omit<HTMLProps<HTMLButtonElement>, "onClick" | "type"> {
76
- value: string;
77
62
  }
78
63
 
79
- const TabTrigger = ({ value, className, ...props }: TabTriggerProps) => {
80
- const { activeTab, setActiveTab, setTargetRef, variant } = useTabsContext();
81
- const isActive = activeTab === value;
82
-
64
+ export function TabsTab({
65
+ className,
66
+ ...props
67
+ }: TabsPrimitive.Tab.Props): React.ReactElement {
83
68
  return (
84
- <button
85
- data-slot="tabs-trigger"
86
- {...props}
87
- type="button"
88
- ref={(element) => {
89
- if (isActive) setTargetRef(element);
90
- }}
91
- onClick={() => setActiveTab(value)}
92
- data-tab={value}
93
- data-selected={isActive}
94
- aria-selected={isActive}
69
+ <TabsPrimitive.Tab
95
70
  className={cn(
96
- "px-2 py-2 cursor-pointer w-full min-w-[100px] text-center rounded-t font-semibold select-none",
97
- variant === "background"
98
- ? "hover:bg-transparent aria-selected:text-primary-foreground rounded"
99
- : "hover:bg-accent hover:text-accent-foreground",
71
+ "relative flex h-9 shrink-0 grow cursor-pointer items-center justify-center gap-1.5 whitespace-nowrap rounded-md border border-transparent px-[calc(--spacing(2.5)-1px)] font-medium text-base outline-none transition-[color,background-color,box-shadow] hover:text-muted-foreground focus-visible:ring-2 focus-visible:ring-ring data-disabled:pointer-events-none data-[orientation=vertical]:w-full data-[orientation=vertical]:grow-0 data-[orientation=vertical]:justify-start data-active:text-foreground data-disabled:opacity-64 group-data-[variant=background]/tabs-list:data-active:text-primary-foreground sm:h-8 sm:text-sm [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:-mx-0.5 [&_svg]:shrink-0",
100
72
  className,
101
73
  )}
74
+ data-slot="tabs-tab"
75
+ {...props}
102
76
  />
103
77
  );
104
- };
105
-
106
- interface TabContentProps extends HTMLProps<HTMLDivElement> {
107
- value: string;
108
- }
109
-
110
- const TabContent = ({ value, ...props }: TabContentProps) => {
111
- const { activeTab } = useTabsContext();
112
- const isActiveTab = activeTab === value;
113
- if (!isActiveTab) return null;
114
- return <div data-slot="tabs-content" {...props} />;
115
- };
116
-
117
- interface TabsIndicatorProps {
118
- targetRef: HTMLButtonElement | null;
119
- indicatorClassName?: string;
120
78
  }
121
79
 
122
- const TabsIndicator = ({
123
- targetRef,
124
- indicatorClassName,
125
- }: TabsIndicatorProps) => {
126
- const indicatorRef = useRef<HTMLDivElement>(null);
127
- const indicator = indicatorRef?.current;
128
- const targetResizeObserver = useRef<ResizeObserver>(null);
129
- const { variant } = useTabsContext();
130
-
131
- const updatePosition = (): void => {
132
- if (!targetRef || !indicator) return;
133
-
134
- const leftDistance = targetRef.offsetLeft;
135
- const width = targetRef.offsetWidth;
136
-
137
- indicator.style.width = width + "px";
138
- indicator.style.left = leftDistance + "px";
139
- };
140
-
141
- useEffect(() => {
142
- if (targetRef) {
143
- updatePosition();
144
- targetResizeObserver.current = new ResizeObserver(updatePosition);
145
- targetResizeObserver.current.observe(targetRef);
146
-
147
- return () => {
148
- targetResizeObserver.current?.disconnect();
149
- };
150
- }
151
- }, [targetRef]);
152
-
80
+ export function TabsPanel({
81
+ className,
82
+ ...props
83
+ }: TabsPrimitive.Panel.Props): React.ReactElement {
153
84
  return (
154
- <div
155
- data-slot="tabs-indicator"
156
- ref={indicatorRef}
157
- className={cn(
158
- "transition-all duration-300 absolute rounded bg-primary",
159
- variant === "background"
160
- ? "inset-0 -z-10 h-full top-0"
161
- : "top-full h-[3px]",
162
- indicatorClassName,
163
- )}
85
+ <TabsPrimitive.Panel
86
+ className={cn("flex-1 outline-none", className)}
87
+ data-slot="tabs-content"
88
+ {...props}
164
89
  />
165
90
  );
166
- };
91
+ }
167
92
 
168
- export { TabContent, TabList, Tabs, TabTrigger };
169
- export type {
170
- TabContentProps,
171
- TabListProps,
172
- TabsExtendedProps,
173
- TabsProps,
174
- TabTriggerProps,
93
+ export {
94
+ TabsPrimitive,
95
+ TabsTab as TabsTrigger,
96
+ TabsPanel as TabsContent,
97
+ TabsList as TabList,
175
98
  };
@@ -1,2 +1 @@
1
1
  export * from "./tag";
2
- export * from "./tag.variants";
@@ -1,170 +1,205 @@
1
- import { Tag } from "../../components";
2
- import { createToastManager, ToastProvider } from "../toast";
3
-
4
- const toastManager = createToastManager();
5
-
6
1
  import type { Meta, StoryObj } from "@storybook/react-vite";
7
- import { Settings, StarIcon, User } from "lucide-react";
8
- import { action } from "storybook/actions";
2
+ import { BadgeCheck, CheckIcon, X } from "lucide-react";
3
+ import { useState } from "react";
4
+ import { Tag } from "../../components";
5
+ import { Loader } from "../loader";
9
6
 
10
7
  /**
11
- * El componente Tag es una herramienta versátil para etiquetar, categorizar y organizar elementos con etiquetas visuales.
8
+ * Displays a tag or a component that looks like a tag.
9
+ * Supports the Base UI `render` prop to swap the root element (e.g. `<a>`, `<button>`)
10
+ * without losing styles or accessibility.
11
+ *
12
+ * See [Base UI useRender docs](https://base-ui.com/react/utils/use-render) for the full `render` prop API.
12
13
  */
13
14
  const meta: Meta<typeof Tag> = {
14
- title: "Data display/Tag",
15
+ title: "Components/Tag",
15
16
  component: Tag,
17
+ parameters: { layout: "centered" },
16
18
  args: {
17
- children: "Lorem ipsum",
19
+ children: "Tag",
18
20
  },
21
+ tags: ["beta"],
19
22
  };
20
23
 
21
24
  export default meta;
22
25
  type Story = StoryObj<typeof Tag>;
23
26
 
24
- /**
25
- * Variante principal del Tag.
26
- * Ideal para destacar información importante o etiquetas primarias.
27
- */
28
- export const Primary: Story = {
29
- args: {
30
- children: "Tag básico",
31
- variant: "primary",
32
- },
33
- };
34
-
35
- /**
36
- * Variante secundaria con un énfasis visual más sutil.
37
- * Perfecta para información complementaria o categorías secundarias.
38
- */
39
- export const Secondary: Story = {
40
- args: {
41
- variant: "secondary",
42
- children: "Tag secundario",
43
- },
44
- };
45
-
46
- /**
47
- * Variante de error para resaltar estados problemáticos o alertas.
48
- * Usar con moderación y solo cuando sea necesario indicar un error o advertencia.
49
- */
50
- export const ErrorVariant: Story = {
51
- args: {
52
- variant: "error",
53
- children: "Tag de error",
54
- },
55
- };
27
+ export const Default: Story = {};
56
28
 
57
29
  /**
58
- * Variante outline con borde y fondo transparente.
59
- * Útil para interfaces minimalistas o cuando se necesita un contraste menor.
30
+ * Use the `variant` prop to change the visual style of the tag.
60
31
  */
61
- export const Outline: Story = {
62
- args: {
63
- variant: "outline",
64
- children: "Tag outline",
65
- },
32
+ export const Variants: Story = {
33
+ render: () => (
34
+ <div className="flex flex-wrap items-center gap-2">
35
+ <Tag>Default</Tag>
36
+ <Tag variant="secondary">Secondary</Tag>
37
+ <Tag variant="error">Error</Tag>
38
+ <Tag variant="outline">Outline</Tag>
39
+ <Tag variant="ghost">Ghost</Tag>
40
+ <Tag variant="link">Link</Tag>
41
+ </div>
42
+ ),
66
43
  };
67
44
 
68
45
  /**
69
- * Tipo redondo o cuadrado .
46
+ * Add `data-icon="inline-start"` or `data-icon="inline-end"` to the icon element
47
+ * to adjust the lateral padding automatically.
48
+ *
49
+ * ```tsx
50
+ * <Tag>
51
+ * <BadgeCheck data-icon="inline-start" />
52
+ * Verified
53
+ * </Tag>
54
+ * ```
70
55
  */
71
- export const Square: Story = {
56
+ export const WithIcon: Story = {
72
57
  render: () => (
73
- <div className="flex w-full gap-2">
74
- <Tag rounded={"default"} color="blue">
75
- Tag rounded default
58
+ <div className="flex flex-wrap items-center gap-2">
59
+ <Tag>
60
+ <BadgeCheck data-icon="inline-start" />
61
+ Verified
76
62
  </Tag>
77
-
78
- <Tag rounded={"full"} color="magenta">
79
- Tag rounded full
63
+ <Tag variant="secondary">
64
+ 8.5
65
+ <CheckIcon data-icon="inline-end" />
80
66
  </Tag>
81
- <Tag rounded={"square"} color="blue">
82
- Tag rounded square
67
+ <Tag variant="error">
68
+ <X data-icon="inline-start" />
69
+ Rejected
83
70
  </Tag>
84
71
  </div>
85
72
  ),
86
73
  };
87
74
 
88
75
  /**
89
- * Los estilos del Tag.
76
+ * Pass `data-icon="inline-start"` or `data-icon="inline-end"` to the `Loader`
77
+ * so its padding matches icon placement conventions.
90
78
  */
91
- export const Styles: Story = {
79
+ export const WithSpinner: Story = {
92
80
  render: () => (
93
- <div className="flex flex-col items-start justify-start gap-2">
94
- <Tag variant={"outline"} color="var(--color-primary)">
95
- Bordeado default
81
+ <div className="flex flex-wrap items-center gap-2">
82
+ <Tag>
83
+ <Loader
84
+ data-icon="inline-start"
85
+ size="xs"
86
+ className="text-primary-foreground"
87
+ />
88
+ Processing
96
89
  </Tag>
97
- <Tag rounded={"full"} variant={"outline"} color="red">
98
- Bordeado redondo
99
- </Tag>
100
- <Tag rounded={"square"}>Solido cuadrado</Tag>
101
- <Tag rounded={"full"} variant={"error"}>
102
- Solido redondo
103
- </Tag>
104
- <Tag rounded={"full"} color="blue" icon={<User />}>
105
- Solido redondo con icono bordeado
106
- </Tag>
107
- <Tag
108
- rounded={"full"}
109
- color="red"
110
- variant={"borderless"}
111
- icon={<Settings />}
112
- >
113
- Coloreado sin borde redondo con icono
114
- </Tag>
115
- <Tag
116
- color="magenta"
117
- variant={"borderless"}
118
- closable
119
- onClose={() => {
120
- action("onClose")();
121
- toastManager.add({ variant: "success", description: "Tag cerrado" });
122
- }}
123
- >
124
- Coloreado sin borde redondo y cerrable
125
- </Tag>
126
- <Tag color="magenta" variant={"borderless"}>
127
- Coloreado sin borde
90
+ <Tag variant="secondary">
91
+ <Loader data-icon="inline-start" size="xs" variant="ghost" />
92
+ Loading
128
93
  </Tag>
129
94
  </div>
130
95
  ),
131
96
  };
132
97
 
133
98
  /**
134
- * Tags con iconos para mejorar la comprensión visual.
135
- * Los iconos deben ser relevantes y mejorar la comunicación del tag.
99
+ * Use the `render` prop to render the tag as a link or any other element.
100
+ * Styles and accessibility semantics from the target element are preserved.
101
+ *
102
+ * ```tsx
103
+ * <Tag render={<a href="/docs" />}>Documentation</Tag>
104
+ * ```
136
105
  */
137
- export const WithIcon: Story = {
138
- args: {
139
- icon: <StarIcon className="w-4 h-4" />,
140
- children: "Tag con icono",
106
+ export const Link: Story = {
107
+ render: () => (
108
+ <div className="flex flex-wrap items-center gap-2">
109
+ <Tag render={<a href="#" />}>Default link</Tag>
110
+ <Tag variant="outline" render={<a href="#" />}>
111
+ Outline link
112
+ </Tag>
113
+ <Tag variant="link" render={<a href="#" />}>
114
+ Underline link
115
+ </Tag>
116
+ </div>
117
+ ),
118
+ parameters: {
119
+ docs: {
120
+ source: {
121
+ code: `<Tag render={<a href="/docs" />}>Documentation</Tag>`,
122
+ },
123
+ },
141
124
  },
142
125
  };
143
126
 
144
127
  /**
145
- * Variante cerrable que permite al usuario eliminar el tag.
146
- * Incluye callback personalizable y feedback visual mediante toast.
147
- *
148
- * Al pasar la propiedad `closable` a true, se debe pasar la propiedad `onClose` con una función que se ejecutará al cerrar el tag.
128
+ * The `closable` and `onClose` props were removed in v2. Dismissal is now achieved
129
+ * by composing a `<button>` child with `data-icon="inline-end"` — the variant handles
130
+ * padding automatically via the `has-data-[icon]` selector.
149
131
  *
150
132
  * ```tsx
151
- * <Tag closable onClose={() => {}}>Tag cerrable</Tag>
133
+ * <Tag>
134
+ * Closable tag
135
+ * <button type="button" data-icon="inline-end" onClick={onClose}>
136
+ * <X className="size-3" />
137
+ * </button>
138
+ * </Tag>
152
139
  * ```
153
140
  */
141
+ const CLOSABLE_TAGS = [
142
+ { id: 1, label: "Default", variant: "default" },
143
+ { id: 2, label: "Secondary", variant: "secondary" },
144
+ { id: 3, label: "Outline", variant: "outline" },
145
+ { id: 4, label: "Error", variant: "error" },
146
+ { id: 5, label: "Ghost", variant: "ghost" },
147
+ ] as const;
148
+
154
149
  export const Closable: Story = {
155
- decorators: [
156
- (Story) => (
157
- <ToastProvider toastManager={toastManager}>
158
- <Story />
159
- </ToastProvider>
160
- ),
161
- ],
162
- args: {
163
- children: "Tag cerrable",
164
- closable: true,
165
- onClose: () => {
166
- action("onClose")();
167
- toastManager.add({ variant: "success", description: "Tag cerrado" });
168
- },
150
+ render: () => {
151
+ const [visible, setVisible] = useState(CLOSABLE_TAGS.map((t) => t.id));
152
+
153
+ return (
154
+ <div className="flex flex-col gap-4">
155
+ <div className="flex flex-wrap items-center gap-2">
156
+ {CLOSABLE_TAGS.filter((t) => visible.includes(t.id)).map((t) => (
157
+ <Tag key={t.id} variant={t.variant}>
158
+ {t.label}
159
+ <button
160
+ type="button"
161
+ data-icon="inline-end"
162
+ className="cursor-pointer"
163
+ onClick={() =>
164
+ setVisible((prev) => prev.filter((id) => id !== t.id))
165
+ }
166
+ >
167
+ <X className="size-3" />
168
+ </button>
169
+ </Tag>
170
+ ))}
171
+ </div>
172
+ {visible.length < CLOSABLE_TAGS.length && (
173
+ <button
174
+ type="button"
175
+ className="text-xs text-muted-foreground underline underline-offset-4 self-start cursor-pointer"
176
+ onClick={() => setVisible(CLOSABLE_TAGS.map((t) => t.id))}
177
+ >
178
+ Reset
179
+ </button>
180
+ )}
181
+ </div>
182
+ );
169
183
  },
170
184
  };
185
+
186
+ /**
187
+ * Override colors with custom Tailwind classes via `className`.
188
+ * Classes merge with the variant defaults through `cn` (tailwind-merge).
189
+ */
190
+ export const CustomColors: Story = {
191
+ render: () => (
192
+ <div className="flex flex-wrap items-center gap-2">
193
+ <Tag className="bg-green-50 text-green-700 dark:bg-green-800/40 dark:text-green-200">
194
+ <BadgeCheck data-icon="inline-start" />
195
+ Success
196
+ </Tag>
197
+ <Tag className="bg-amber-50 text-amber-700 dark:bg-amber-800/40 dark:text-amber-200">
198
+ Pending
199
+ </Tag>
200
+ <Tag className="bg-blue-50 text-blue-700 dark:bg-blue-800/40 dark:text-blue-200">
201
+ Info
202
+ </Tag>
203
+ </div>
204
+ ),
205
+ };