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

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boxcustodia/library",
3
- "version": "2.0.0-alpha.12",
3
+ "version": "2.0.0-alpha.13",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.es.js",
@@ -232,12 +232,12 @@ La prop `containerClassName` fue eliminada. Usá `className` para estilizar el i
232
232
 
233
233
  ---
234
234
 
235
- ## 3. Version 2.0.0-alpha.8
235
+ ## 4. Version 2.0.0-alpha.11
236
236
 
237
237
  ### Instalación
238
238
 
239
239
  ```bash
240
- npm install @boxcustodia/library@2.0.0-alpha.8
240
+ npm install @boxcustodia/library@2.0.0-alpha.11
241
241
  ```
242
242
 
243
243
  ### Cambios necesarios
@@ -260,18 +260,6 @@ import { useForm, Form, FormField } from "@/components/form/form";
260
260
 
261
261
  El resto de tu código no necesita ningún cambio.
262
262
 
263
- ---
264
-
265
- ## 4. Version 2.0.0-alpha.10
266
-
267
- ### Instalación
268
-
269
- ```bash
270
- npm install @boxcustodia/library@2.0.0-alpha.10
271
- ```
272
-
273
- ### Cambios necesarios
274
-
275
263
  #### Calendar
276
264
 
277
265
  El componente `Calendar` fue completamente reescrito usando `react-day-picker` en lugar de una implementación custom. La API cambió significativamente:
@@ -391,18 +379,6 @@ El componente ahora soporta múltiples modos y cambió de API internamente.
391
379
 
392
380
  **Si necesitás un date picker editable:** Usá el nuevo componente `DateInput` que permite escribir la fecha directamente en el input además de seleccionar desde el calendar.
393
381
 
394
- ---
395
-
396
- ## 5. Version 2.0.0-alpha.11
397
-
398
- ### Instalación
399
-
400
- ```bash
401
- npm install @boxcustodia/library@2.0.0-alpha.11
402
- ```
403
-
404
- ### Cambios necesarios
405
-
406
382
  #### Combobox
407
383
 
408
384
  El Combobox fue completamente reescrito usando Base UI en lugar de la arquitectura anterior. La API es significativamente diferente. Ahora soporta selección múltiple con chips dinámicos y una arquitectura de primitivas para casos de uso avanzados.
@@ -1,4 +1,5 @@
1
1
  import { Combobox as ComboboxPrimitive } from "@base-ui/react/combobox";
2
+ import { Field as FieldPrimitive } from "@base-ui/react/field";
2
3
  import { ChevronsUpDownIcon, XIcon } from "lucide-react";
3
4
  import * as React from "react";
4
5
  import { cn } from "../../lib";
@@ -487,31 +488,36 @@ export function ComboboxChips({
487
488
  const { chipsRef } = React.useContext(ComboboxContext);
488
489
 
489
490
  return (
490
- <ComboboxPrimitive.Chips
491
- className={cn(
492
- inputBaseClasses,
493
- "relative inline-flex pr-6 w-full flex-wrap gap-1",
494
- "placeholder:text-muted-foreground",
495
- "focus-within:border-ring",
496
- "has-aria-invalid:border-error focus-within:has-aria-invalid:ring-error/20",
497
- "has-disabled:cursor-not-allowed has-disabled:opacity-50",
498
- className,
499
- )}
500
- data-slot="combobox-chips"
501
- ref={chipsRef as React.Ref<HTMLDivElement> | null}
502
- {...props}
503
- >
504
- {startAddon && (
505
- <div
506
- aria-hidden="true"
507
- className="flex shrink-0 items-center ps-2 opacity-80 has-[+[data-slot=combobox-chip]]:pe-2 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:-ms-0.5 [&_svg]:-me-1.5"
508
- data-slot="combobox-start-addon"
491
+ <FieldPrimitive.Validity>
492
+ {({ validity }) => (
493
+ <ComboboxPrimitive.Chips
494
+ aria-invalid={validity.valid === false ? true : undefined}
495
+ className={cn(
496
+ inputBaseClasses,
497
+ "relative inline-flex pr-6 w-full flex-wrap gap-1",
498
+ "placeholder:text-muted-foreground",
499
+ "focus-within:border-ring",
500
+ "aria-invalid:border-error focus-within:aria-invalid:ring-error/20",
501
+ "has-disabled:cursor-not-allowed has-disabled:opacity-50",
502
+ className,
503
+ )}
504
+ data-slot="combobox-chips"
505
+ ref={chipsRef as React.Ref<HTMLDivElement> | null}
506
+ {...props}
509
507
  >
510
- {startAddon}
511
- </div>
508
+ {startAddon && (
509
+ <div
510
+ aria-hidden="true"
511
+ className="flex shrink-0 items-center ps-2 opacity-80 has-[+[data-slot=combobox-chip]]:pe-2 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:-ms-0.5 [&_svg]:-me-1.5"
512
+ data-slot="combobox-start-addon"
513
+ >
514
+ {startAddon}
515
+ </div>
516
+ )}
517
+ {children}
518
+ </ComboboxPrimitive.Chips>
512
519
  )}
513
- {children}
514
- </ComboboxPrimitive.Chips>
520
+ </FieldPrimitive.Validity>
515
521
  );
516
522
  }
517
523
 
@@ -1,70 +1,139 @@
1
- import { Meta, StoryObj } from "@storybook/react-vite";
2
- import { CatIcon, Github, PencilIcon } from "lucide-react";
3
- import { Button, Divider } from "../../components";
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { Divider } from "../../components";
4
3
 
4
+ /**
5
+ * Visual separator between sections or inline elements.
6
+ *
7
+ * Built on the Base UI `Separator` primitive. Supports `orientation` (`horizontal` | `vertical`)
8
+ * and a `render` prop for polymorphic composition. The DOM element receives `data-orientation`
9
+ * which can be used as a CSS selector for conditional styles.
10
+ */
5
11
  const meta: Meta<typeof Divider> = {
6
- title: "others/Divider",
12
+ title: "Components/Divider",
7
13
  component: Divider,
14
+ args: {
15
+ orientation: "horizontal",
16
+ },
17
+ parameters: {
18
+ layout: "centered",
19
+ },
8
20
  };
9
21
 
10
22
  export default meta;
11
- type Story = StoryObj<typeof meta>;
23
+ type Story = StoryObj<typeof Divider>;
12
24
 
25
+ export const Default: Story = {
26
+ render: () => (
27
+ <div className="w-72 rounded-lg border p-4 text-sm">
28
+ <div className="flex items-center justify-between">
29
+ <span className="font-medium">Deployment #847</span>
30
+ <span className="text-xs text-green-500">● Live</span>
31
+ </div>
32
+ <Divider className="my-3" />
33
+ <div className="space-y-1.5 text-muted-foreground">
34
+ <div className="flex justify-between">
35
+ <span>Triggered by</span>
36
+ <span className="text-foreground">fbaigorria</span>
37
+ </div>
38
+ <div className="flex justify-between">
39
+ <span>Branch</span>
40
+ <span className="text-foreground">develop</span>
41
+ </div>
42
+ <div className="flex justify-between">
43
+ <span>Duration</span>
44
+ <span className="text-foreground">1m 43s</span>
45
+ </div>
46
+ </div>
47
+ <Divider className="my-3" />
48
+ <div className="flex items-center justify-center gap-4 text-muted-foreground">
49
+ <span>2 warnings</span>
50
+ <Divider orientation="vertical" className="h-4" />
51
+ <span>0 errors</span>
52
+ <Divider orientation="vertical" className="h-4" />
53
+ <span>127 tests</span>
54
+ </div>
55
+ </div>
56
+ ),
57
+ };
58
+
59
+ /**
60
+ * Default orientation. Renders a full-width 1px horizontal line via `data-[orientation=horizontal]`.
61
+ * Commonly used to separate named sections inside a panel or card.
62
+ */
13
63
  export const Horizontal: Story = {
64
+ render: () => (
65
+ <div className="w-72 rounded-lg border p-4 space-y-4">
66
+ <div>
67
+ <p className="text-sm font-medium">Profile</p>
68
+ <p className="text-xs text-muted-foreground">Franco Baigorria</p>
69
+ </div>
70
+ <Divider />
71
+ <div>
72
+ <p className="text-sm font-medium">Team</p>
73
+ <p className="text-xs text-muted-foreground">Engineering</p>
74
+ </div>
75
+ <Divider />
76
+ <div>
77
+ <p className="text-sm font-medium">Role</p>
78
+ <p className="text-xs text-muted-foreground">Frontend Architect</p>
79
+ </div>
80
+ </div>
81
+ ),
82
+ };
83
+
84
+ /**
85
+ * In vertical orientation the component uses `self-stretch` to fill the full height of
86
+ * the flex container. Wrapping in a `div` with `flex` and a defined height is the expected pattern.
87
+ */
88
+ export const Vertical: Story = {
14
89
  args: {
15
- orientation: "horizontal",
90
+ orientation: "vertical",
16
91
  },
17
- decorators: [
18
- (Story) => (
19
- <div className="w-20 m-auto rounded flex flex-col items-center gap-2">
20
- <Button size="icon">
21
- <PencilIcon />
22
- </Button>
23
- <Story />
24
- <Button size="icon">
25
- <CatIcon />
26
- </Button>
27
- <Story />
28
- <Button size="icon">
29
- <Github />
30
- </Button>
31
- </div>
32
- ),
33
- ],
92
+ render: (args) => (
93
+ <div className="flex h-10 items-center gap-4">
94
+ <span className="text-sm">Section A</span>
95
+ <Divider {...args} />
96
+ <span className="text-sm">Section B</span>
97
+ <Divider {...args} />
98
+ <span className="text-sm">Section C</span>
99
+ </div>
100
+ ),
34
101
  };
35
102
 
36
103
  /**
37
- * Para poder desplegar el componente `Divider` de manera vertical, se debe utilizar el atributo `orientation` con el valor `vertical`
38
- * y setear una altura en el contenedor
104
+ * The `render` prop replaces the rendered DOM element. Useful for swapping semantics
105
+ * to `<hr>` or any other element without losing component styles.
39
106
  *
40
107
  * ```tsx
41
- * <div className="h-16"> // <- Altura fija
42
- * <PencilIcon />
43
- * <Divider orientation="vertical" /> // <- vertical
44
- * <CatIcon />
45
- * </div>
108
+ * <Divider render={<hr />} />
46
109
  * ```
47
110
  */
48
-
49
- export const Vertical: Story = {
50
- args: {
51
- orientation: "vertical",
111
+ export const Render: Story = {
112
+ render: (args) => (
113
+ <div className="w-64">
114
+ <Divider {...args} render={<hr />} />
115
+ </div>
116
+ ),
117
+ parameters: {
118
+ docs: {
119
+ source: {
120
+ code: `<Divider render={<hr />} />`,
121
+ },
122
+ },
52
123
  },
53
- decorators: [
54
- (Story) => (
55
- <div className="h-16 w-36 m-auto rounded flex items-center gap-2">
56
- <Button size="icon">
57
- <PencilIcon />
58
- </Button>
59
- <Story />
60
- <Button size="icon">
61
- <CatIcon />
62
- </Button>
63
- <Story />
64
- <Button size="icon">
65
- <Github />
66
- </Button>
67
- </div>
68
- ),
69
- ],
124
+ };
125
+
126
+ /**
127
+ * `className` accepts Tailwind classes to override color, thickness, or spacing.
128
+ * `data-[orientation=horizontal]` and `data-[orientation=vertical]` are available
129
+ * as CSS selectors for advanced conditional styles.
130
+ */
131
+ export const Custom: Story = {
132
+ render: () => (
133
+ <div className="space-y-4 w-64">
134
+ <Divider className="bg-primary" />
135
+ <Divider className="bg-error" />
136
+ <Divider className="border-t border-dashed border-input bg-transparent" />
137
+ </div>
138
+ ),
70
139
  };
@@ -1,23 +1,23 @@
1
- import { HTMLProps } from "react";
2
- import { cn } from "../../lib";
3
-
4
- type Props = {
5
- orientation?: "horizontal" | "vertical";
6
- } & HTMLProps<HTMLHRElement>;
7
-
8
- export const Divider = (props: Props) => {
9
- const { className, orientation = "horizontal" } = props;
1
+ import { Separator as SeparatorPrimitive } from "@base-ui/react/separator";
2
+ import type React from "react";
3
+ import { cn } from "tailwind-variants";
10
4
 
5
+ export function Divider({
6
+ className,
7
+ orientation = "horizontal",
8
+ ...props
9
+ }: SeparatorPrimitive.Props): React.ReactElement {
11
10
  return (
12
- <hr
13
- {...props}
14
- aria-orientation={orientation}
11
+ <SeparatorPrimitive
15
12
  className={cn(
16
- "shrink-0 bg-input",
17
- orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
13
+ "shrink-0 bg-input data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px data-[orientation=vertical]:not-[[class^='h-']]:not-[[class*='_h-']]:self-stretch",
18
14
  className,
19
15
  )}
20
- data-slot="divider"
16
+ data-slot="separator"
17
+ orientation={orientation}
18
+ {...props}
21
19
  />
22
20
  );
23
- };
21
+ }
22
+
23
+ export { SeparatorPrimitive };
@@ -127,7 +127,7 @@ export const WithValidity: Story = {
127
127
  <FieldValidity>
128
128
  {({ validity }) =>
129
129
  !validity.valid && (
130
- <p className="text-destructive-foreground text-xs">
130
+ <p className="text-error-foreground text-xs">
131
131
  {validity.valueMissing
132
132
  ? "Email is required."
133
133
  : "Enter a valid email address."}
@@ -11,7 +11,7 @@ const generateRandomOption = () => ({
11
11
  const items = faker.helpers.multiple(generateRandomOption, { count: 10 });
12
12
 
13
13
  const meta: Meta<typeof MultiSelect> = {
14
- title: "Data entry/MultiSelect",
14
+ title: "Deprecated/MultiSelect",
15
15
  component: MultiSelect,
16
16
  args: {
17
17
  items,
@@ -32,11 +32,12 @@ export function NumberInputGroup({
32
32
  return (
33
33
  <NumberFieldPrimitive.Group
34
34
  className={cn(
35
- "flex w-full items-center overflow-hidden rounded-md border border-input bg-background text-sm transition-shadow",
36
- "focus-within:border-ring",
37
- "aria-invalid:border-error focus-within:aria-invalid:ring-error/20",
38
- "has-aria-invalid:border-error focus-within:has-aria-invalid:ring-error/20",
39
- "data-[invalid]:border-error focus-within:data-[invalid]:ring-error/20",
35
+ "flex h-10 w-full items-center overflow-hidden rounded-md bg-background text-sm transition-shadow",
36
+ "outline outline-1 outline-input [outline-offset:-1px]",
37
+ "focus-within:outline-ring",
38
+ "aria-invalid:outline-error focus-within:aria-invalid:ring-error/20",
39
+ "has-aria-invalid:outline-error focus-within:has-aria-invalid:ring-error/20",
40
+ "data-[invalid]:outline-error focus-within:data-[invalid]:ring-error/20",
40
41
  "data-disabled:cursor-not-allowed data-disabled:opacity-50 data-disabled:pointer-events-none",
41
42
  "[&_svg]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
42
43
  className,
@@ -195,6 +196,7 @@ export function NumberInput({
195
196
  {...(invalid ? { "aria-invalid": true } : {})}
196
197
  placeholder={placeholder}
197
198
  {...inputProps}
199
+ className={cn("self-stretch", inputProps?.className)}
198
200
  />
199
201
  {!hideControls && (
200
202
  <div className="flex flex-col self-stretch divide-y divide-input border-l border-input">
@@ -37,10 +37,7 @@ export function Password({
37
37
  return (
38
38
  <div
39
39
  className={cn(
40
- "relative inline-flex w-full rounded-md border border-input bg-background",
41
- "text-sm transition-shadow",
42
- "has-focus-visible:border-ring",
43
- "has-aria-invalid:border-error has-focus-visible:has-aria-invalid:ring-error/20",
40
+ "relative inline-flex w-full",
44
41
  "has-disabled:cursor-not-allowed has-disabled:opacity-50",
45
42
  className,
46
43
  )}
@@ -50,7 +47,14 @@ export function Password({
50
47
  {...rest}
51
48
  onChange={handleChange}
52
49
  type={showPassword ? "text" : "password"}
53
- className="w-full min-w-0 rounded-[inherit] bg-transparent py-2 pl-3 pr-10 text-sm outline-none placeholder:text-muted-foreground [&::-ms-clear]:hidden [&::-ms-reveal]:hidden"
50
+ className={cn(
51
+ "h-10 w-full min-w-0 rounded-md border border-input bg-background",
52
+ "pl-3 pr-10 text-sm outline-none transition-shadow placeholder:text-muted-foreground",
53
+ "focus-visible:border-ring",
54
+ "aria-invalid:border-error focus-visible:aria-invalid:ring-error/20",
55
+ "disabled:cursor-not-allowed",
56
+ "[&::-ms-clear]:hidden [&::-ms-reveal]:hidden",
57
+ )}
54
58
  />
55
59
  <button
56
60
  data-slot="password-toggle"