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

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 (174) hide show
  1. package/dist/index.cjs.js +1 -138
  2. package/dist/index.d.ts +1083 -717
  3. package/dist/index.es.js +7059 -56179
  4. package/dist/theme.css +1 -1
  5. package/package.json +34 -26
  6. package/src/__doc__/Changelog.mdx +6 -6
  7. package/src/__doc__/Examples.tsx +1 -1
  8. package/src/__doc__/Intro.mdx +3 -3
  9. package/src/__doc__/Tabs.mdx +112 -0
  10. package/src/__doc__/V2.mdx +1245 -0
  11. package/src/components/accordion/accordion.stories.tsx +143 -0
  12. package/src/components/accordion/accordion.tsx +135 -0
  13. package/src/components/accordion/index.ts +1 -0
  14. package/src/components/alert/alert.stories.tsx +24 -4
  15. package/src/components/alert/alert.tsx +17 -9
  16. package/src/components/alert-dialog/alert-dialog.stories.tsx +24 -0
  17. package/src/components/alert-dialog/alert-dialog.test.tsx +1 -1
  18. package/src/components/alert-dialog/alert-dialog.tsx +58 -10
  19. package/src/components/auto-complete/auto-complete.stories.tsx +615 -200
  20. package/src/components/auto-complete/auto-complete.tsx +420 -68
  21. package/src/components/auto-complete/index.ts +0 -1
  22. package/src/components/avatar/avatar.stories.tsx +162 -21
  23. package/src/components/avatar/avatar.tsx +79 -20
  24. package/src/components/button/button.stories.tsx +236 -294
  25. package/src/components/button/button.test.tsx +10 -17
  26. package/src/components/button/button.tsx +53 -18
  27. package/src/components/button/components/base-button.tsx +25 -53
  28. package/src/components/button/index.ts +0 -1
  29. package/src/components/calendar/calendar.stories.tsx +1 -1
  30. package/src/components/calendar/calendar.tsx +4 -4
  31. package/src/components/card/card.stories.tsx +140 -69
  32. package/src/components/card/card.tsx +155 -54
  33. package/src/components/center/center.stories.tsx +22 -39
  34. package/src/components/checkbox/checkbox.stories.tsx +25 -5
  35. package/src/components/checkbox/checkbox.tsx +76 -15
  36. package/src/components/checkbox-group/checkbox-group.stories.tsx +116 -28
  37. package/src/components/checkbox-group/checkbox-group.tsx +84 -3
  38. package/src/components/combobox/combobox.stories.tsx +33 -23
  39. package/src/components/combobox/combobox.tsx +120 -104
  40. package/src/components/date-picker/date-input.stories.tsx +14 -6
  41. package/src/components/date-picker/date-input.tsx +3 -3
  42. package/src/components/date-picker/date-picker.model.ts +13 -4
  43. package/src/components/date-picker/date-picker.stories.tsx +38 -12
  44. package/src/components/date-picker/date-picker.tsx +29 -15
  45. package/src/components/dialog/dialog.stories.tsx +18 -0
  46. package/src/components/dialog/dialog.test.tsx +1 -1
  47. package/src/components/dialog/dialog.tsx +51 -20
  48. package/src/components/divider/divider.stories.tsx +6 -0
  49. package/src/components/dropzone/dropzone.stories.tsx +70 -90
  50. package/src/components/dropzone/dropzone.tsx +383 -105
  51. package/src/components/dropzone/index.ts +0 -1
  52. package/src/components/empty/empty.stories.tsx +164 -0
  53. package/src/components/empty/empty.tsx +156 -0
  54. package/src/components/empty/index.ts +1 -0
  55. package/src/components/field/field.stories.tsx +226 -3
  56. package/src/components/field/field.tsx +77 -42
  57. package/src/components/form/form.stories.tsx +320 -197
  58. package/src/components/form/form.tsx +3 -23
  59. package/src/components/index.ts +2 -6
  60. package/src/components/input/input.stories.tsx +5 -5
  61. package/src/components/input/input.tsx +5 -5
  62. package/src/components/kbd/kbd.stories.tsx +1 -0
  63. package/src/components/label/label.stories.tsx +16 -0
  64. package/src/components/label/label.tsx +13 -2
  65. package/src/components/loader/loader.stories.tsx +7 -5
  66. package/src/components/loader/loader.tsx +8 -3
  67. package/src/components/menu/menu-primitives.tsx +207 -196
  68. package/src/components/menu/menu.stories.tsx +275 -146
  69. package/src/components/menu/menu.tsx +146 -54
  70. package/src/components/number-input/number-input.stories.tsx +27 -4
  71. package/src/components/number-input/number-input.test.tsx +2 -2
  72. package/src/components/number-input/number-input.tsx +29 -33
  73. package/src/components/otp/index.ts +1 -0
  74. package/src/components/otp/otp.stories.tsx +209 -0
  75. package/src/components/otp/otp.tsx +100 -0
  76. package/src/components/pagination/index.ts +1 -0
  77. package/src/components/pagination/pagination.model.ts +2 -0
  78. package/src/components/pagination/pagination.stories.tsx +153 -59
  79. package/src/components/pagination/pagination.test.tsx +122 -57
  80. package/src/components/pagination/pagination.tsx +575 -77
  81. package/src/components/password/password.stories.tsx +18 -3
  82. package/src/components/password/password.tsx +26 -10
  83. package/src/components/popover/popover.stories.tsx +26 -5
  84. package/src/components/popover/popover.tsx +15 -23
  85. package/src/components/progress/progress.stories.tsx +1 -0
  86. package/src/components/radio-group/index.ts +1 -0
  87. package/src/components/radio-group/radio-group.stories.tsx +251 -0
  88. package/src/components/radio-group/radio-group.tsx +212 -0
  89. package/src/components/scroll-area/scroll-area.stories.tsx +1 -0
  90. package/src/components/select/select.stories.tsx +118 -19
  91. package/src/components/select/select.tsx +67 -62
  92. package/src/components/skeleton/skeleton.stories.tsx +1 -0
  93. package/src/components/stack/stack.stories.tsx +179 -89
  94. package/src/components/stack/stack.tsx +2 -2
  95. package/src/components/stepper/index.ts +1 -1
  96. package/src/components/stepper/stepper.stories.tsx +766 -83
  97. package/src/components/stepper/stepper.test.tsx +18 -18
  98. package/src/components/stepper/stepper.tsx +554 -0
  99. package/src/components/switch/switch.stories.tsx +15 -1
  100. package/src/components/switch/switch.tsx +17 -4
  101. package/src/components/table/index.ts +0 -2
  102. package/src/components/table/table.stories.tsx +131 -18
  103. package/src/components/table/table.test.tsx +1 -1
  104. package/src/components/table/table.tsx +183 -77
  105. package/src/components/tabs/tabs.stories.tsx +372 -155
  106. package/src/components/tabs/tabs.test.tsx +12 -12
  107. package/src/components/tabs/tabs.tsx +72 -149
  108. package/src/components/tag/index.ts +0 -1
  109. package/src/components/tag/tag.stories.tsx +147 -120
  110. package/src/components/tag/tag.tsx +47 -95
  111. package/src/components/textarea/textarea.stories.tsx +8 -22
  112. package/src/components/textarea/textarea.tsx +17 -79
  113. package/src/components/timeline/timeline.stories.tsx +322 -42
  114. package/src/components/timeline/timeline.tsx +359 -132
  115. package/src/components/toast/toast.stories.tsx +1 -0
  116. package/src/components/tooltip/tooltip.tsx +11 -9
  117. package/src/components/tree/index.ts +0 -1
  118. package/src/components/tree/tree.stories.tsx +364 -408
  119. package/src/components/tree/tree.test.tsx +163 -0
  120. package/src/components/tree/tree.tsx +212 -36
  121. package/src/hooks/useAsync/__doc__/useAsync.stories.tsx +5 -5
  122. package/src/hooks/useClipboard/__doc__/useClipboard.stories.tsx +1 -3
  123. package/src/hooks/useDebounceCallback/__doc__/useDebouncedCallback.stories.tsx +6 -6
  124. package/src/hooks/useDocumentTitle/__doc__/useDocumentTitle.stories.tsx +1 -1
  125. package/src/hooks/useEventListener/__test__/useEventListener.test.tsx +1 -1
  126. package/src/hooks/useLocalStorage/__doc__/useLocalStorage.stories.tsx +1 -1
  127. package/src/hooks/usePagination/usePagination.tsx +36 -24
  128. package/src/styles/theme.css +1 -1
  129. package/src/utils/form.tsx +69 -37
  130. package/src/utils/index.ts +1 -1
  131. package/src/__doc__/Migration.mdx +0 -451
  132. package/src/components/auto-complete/auto-complete-primitives.tsx +0 -155
  133. package/src/components/background-image/background-image.stories.tsx +0 -21
  134. package/src/components/background-image/background-image.test.tsx +0 -29
  135. package/src/components/background-image/background-image.tsx +0 -23
  136. package/src/components/background-image/index.ts +0 -1
  137. package/src/components/button/button.variants.ts +0 -44
  138. package/src/components/button/components/loader-overlay.tsx +0 -21
  139. package/src/components/button/components/loading-icon.tsx +0 -47
  140. package/src/components/dropzone/upload-primitives.tsx +0 -310
  141. package/src/components/dropzone/use-dropzone.ts +0 -122
  142. package/src/components/empty-state/empty-state.stories.tsx +0 -56
  143. package/src/components/empty-state/empty-state.tsx +0 -39
  144. package/src/components/empty-state/index.ts +0 -1
  145. package/src/components/heading/heading.stories.tsx +0 -74
  146. package/src/components/heading/heading.tsx +0 -28
  147. package/src/components/heading/heading.variants.ts +0 -27
  148. package/src/components/heading/index.ts +0 -1
  149. package/src/components/kbd/kbd.variants.ts +0 -26
  150. package/src/components/menu/util/render-menu-item.tsx +0 -54
  151. package/src/components/multi-select/hooks/use-multi-select.ts +0 -66
  152. package/src/components/multi-select/index.ts +0 -1
  153. package/src/components/multi-select/multi-select.stories.tsx +0 -294
  154. package/src/components/multi-select/multi-select.tsx +0 -300
  155. package/src/components/multi-select/multi-select.variants.ts +0 -22
  156. package/src/components/pagination/components/pagination-option.tsx +0 -27
  157. package/src/components/show/index.ts +0 -1
  158. package/src/components/show/show.stories.tsx +0 -197
  159. package/src/components/show/show.test.tsx +0 -41
  160. package/src/components/show/show.tsx +0 -16
  161. package/src/components/stepper/Stepper.tsx +0 -190
  162. package/src/components/stepper/context/stepper-context.tsx +0 -11
  163. package/src/components/table/table-primitives.tsx +0 -122
  164. package/src/components/table/table.model.ts +0 -20
  165. package/src/components/table-pagination/index.ts +0 -2
  166. package/src/components/table-pagination/table-pagination.model.ts +0 -2
  167. package/src/components/table-pagination/table-pagination.stories.tsx +0 -23
  168. package/src/components/table-pagination/table-pagination.test.tsx +0 -32
  169. package/src/components/table-pagination/table-pagination.tsx +0 -108
  170. package/src/components/tabs/context/tabs-context.tsx +0 -14
  171. package/src/components/tag/tag.variants.ts +0 -31
  172. package/src/components/timeline/timeline-status.ts +0 -5
  173. package/src/components/tree/hooks/use-controllable-tree-state.ts +0 -80
  174. 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,197 @@
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
  },
19
21
  };
20
22
 
21
23
  export default meta;
22
24
  type Story = StoryObj<typeof Tag>;
23
25
 
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
- };
26
+ export const Default: Story = {};
56
27
 
57
28
  /**
58
- * Variante outline con borde y fondo transparente.
59
- * Útil para interfaces minimalistas o cuando se necesita un contraste menor.
29
+ * Use the `variant` prop to change the visual style of the tag.
60
30
  */
61
- export const Outline: Story = {
62
- args: {
63
- variant: "outline",
64
- children: "Tag outline",
65
- },
31
+ export const Variants: Story = {
32
+ render: () => (
33
+ <div className="flex flex-wrap items-center gap-2">
34
+ <Tag>Default</Tag>
35
+ <Tag variant="secondary">Secondary</Tag>
36
+ <Tag variant="error">Error</Tag>
37
+ <Tag variant="outline">Outline</Tag>
38
+ <Tag variant="ghost">Ghost</Tag>
39
+ <Tag variant="link">Link</Tag>
40
+ </div>
41
+ ),
66
42
  };
67
43
 
68
44
  /**
69
- * Tipo redondo o cuadrado .
45
+ * Compose icons directly as children — JSX order determines position.
46
+ *
47
+ * ```tsx
48
+ * <Tag>
49
+ * <BadgeCheck />
50
+ * Verified
51
+ * </Tag>
52
+ * ```
70
53
  */
71
- export const Square: Story = {
54
+ export const WithIcon: Story = {
72
55
  render: () => (
73
- <div className="flex w-full gap-2">
74
- <Tag rounded={"default"} color="blue">
75
- Tag rounded default
56
+ <div className="flex flex-wrap items-center gap-2">
57
+ <Tag>
58
+ <BadgeCheck />
59
+ Verified
76
60
  </Tag>
77
-
78
- <Tag rounded={"full"} color="magenta">
79
- Tag rounded full
61
+ <Tag variant="secondary">
62
+ 8.5
63
+ <CheckIcon />
80
64
  </Tag>
81
- <Tag rounded={"square"} color="blue">
82
- Tag rounded square
65
+ <Tag variant="error">
66
+ <X />
67
+ Rejected
83
68
  </Tag>
84
69
  </div>
85
70
  ),
86
71
  };
87
72
 
88
73
  /**
89
- * Los estilos del Tag.
74
+ * Drop a `Loader` as a child to indicate async state. Same composition
75
+ * rules as any other icon — JSX order determines position.
90
76
  */
91
- export const Styles: Story = {
77
+ export const WithSpinner: Story = {
92
78
  render: () => (
93
- <div className="flex flex-col items-start justify-start gap-2">
94
- <Tag variant={"outline"} color="var(--color-primary)">
95
- Bordeado default
79
+ <div className="flex flex-wrap items-center gap-2">
80
+ <Tag>
81
+ <Loader size="xs" className="text-primary-foreground" />
82
+ Processing
96
83
  </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
84
+ <Tag variant="secondary">
85
+ <Loader size="xs" variant="ghost" />
86
+ Loading
128
87
  </Tag>
129
88
  </div>
130
89
  ),
131
90
  };
132
91
 
133
92
  /**
134
- * Tags con iconos para mejorar la comprensión visual.
135
- * Los iconos deben ser relevantes y mejorar la comunicación del tag.
93
+ * Use the `render` prop to render the tag as a link or any other element.
94
+ * Styles and accessibility semantics from the target element are preserved.
95
+ *
96
+ * ```tsx
97
+ * <Tag render={<a href="/docs" />}>Documentation</Tag>
98
+ * ```
136
99
  */
137
- export const WithIcon: Story = {
138
- args: {
139
- icon: <StarIcon className="w-4 h-4" />,
140
- children: "Tag con icono",
100
+ export const Link: Story = {
101
+ render: () => (
102
+ <div className="flex flex-wrap items-center gap-2">
103
+ <Tag render={<a href="#" />}>Default link</Tag>
104
+ <Tag variant="outline" render={<a href="#" />}>
105
+ Outline link
106
+ </Tag>
107
+ <Tag variant="link" render={<a href="#" />}>
108
+ Underline link
109
+ </Tag>
110
+ </div>
111
+ ),
112
+ parameters: {
113
+ docs: {
114
+ source: {
115
+ code: `<Tag render={<a href="/docs" />}>Documentation</Tag>`,
116
+ },
117
+ },
141
118
  },
142
119
  };
143
120
 
144
121
  /**
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.
122
+ * The `closable` and `onClose` props were removed in v2. Dismissal is now
123
+ * achieved by composing a `<button>` child with the close icon.
149
124
  *
150
125
  * ```tsx
151
- * <Tag closable onClose={() => {}}>Tag cerrable</Tag>
126
+ * <Tag>
127
+ * Closable tag
128
+ * <button type="button" onClick={onClose}>
129
+ * <X className="size-3" />
130
+ * </button>
131
+ * </Tag>
152
132
  * ```
153
133
  */
134
+ const CLOSABLE_TAGS = [
135
+ { id: 1, label: "Default", variant: "default" },
136
+ { id: 2, label: "Secondary", variant: "secondary" },
137
+ { id: 3, label: "Outline", variant: "outline" },
138
+ { id: 4, label: "Error", variant: "error" },
139
+ { id: 5, label: "Ghost", variant: "ghost" },
140
+ ] as const;
141
+
154
142
  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
- },
143
+ render: () => {
144
+ const [visible, setVisible] = useState(CLOSABLE_TAGS.map((t) => t.id));
145
+
146
+ return (
147
+ <div className="flex flex-col gap-4">
148
+ <div className="flex flex-wrap items-center gap-2">
149
+ {CLOSABLE_TAGS.filter((t) => visible.includes(t.id)).map((t) => (
150
+ <Tag key={t.id} variant={t.variant}>
151
+ {t.label}
152
+ <button
153
+ type="button"
154
+ className="cursor-pointer"
155
+ onClick={() =>
156
+ setVisible((prev) => prev.filter((id) => id !== t.id))
157
+ }
158
+ >
159
+ <X className="size-3" />
160
+ </button>
161
+ </Tag>
162
+ ))}
163
+ </div>
164
+ {visible.length < CLOSABLE_TAGS.length && (
165
+ <button
166
+ type="button"
167
+ className="text-xs text-muted-foreground underline underline-offset-4 self-start cursor-pointer"
168
+ onClick={() => setVisible(CLOSABLE_TAGS.map((t) => t.id))}
169
+ >
170
+ Reset
171
+ </button>
172
+ )}
173
+ </div>
174
+ );
169
175
  },
170
176
  };
177
+
178
+ /**
179
+ * Override colors with custom Tailwind classes via `className`.
180
+ * Classes merge with the variant defaults through `cn` (tailwind-merge).
181
+ */
182
+ export const CustomColors: Story = {
183
+ render: () => (
184
+ <div className="flex flex-wrap items-center gap-2">
185
+ <Tag className="bg-green-50 text-green-700 dark:bg-green-800/40 dark:text-green-200">
186
+ <BadgeCheck />
187
+ Success
188
+ </Tag>
189
+ <Tag className="bg-amber-50 text-amber-700 dark:bg-amber-800/40 dark:text-amber-200">
190
+ Pending
191
+ </Tag>
192
+ <Tag className="bg-blue-50 text-blue-700 dark:bg-blue-800/40 dark:text-blue-200">
193
+ Info
194
+ </Tag>
195
+ </div>
196
+ ),
197
+ };