@lindaui/blocks 0.0.2 → 0.1.0

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.
@@ -3,6 +3,12 @@ export interface DocumentPanelProps {
3
3
  title: string;
4
4
  subtitle?: ReactNode;
5
5
  meta?: ReactNode;
6
+ /**
7
+ * `block` (default) stacks subtitle and meta on their own lines. `inline`
8
+ * flows them on one wrapping row (subtitle · meta) so a date sits next to the
9
+ * subtitle instead of forcing a new line.
10
+ */
11
+ metaPlacement?: "block" | "inline";
6
12
  statusBadge?: ReactNode;
7
13
  state: "empty" | "loading" | "ready";
8
14
  emptyContent?: ReactNode;
@@ -11,5 +17,5 @@ export interface DocumentPanelProps {
11
17
  children?: ReactNode;
12
18
  footer?: ReactNode;
13
19
  }
14
- export declare function DocumentPanel({ title, subtitle, meta, statusBadge, state, emptyContent, loadingLabel, notices, children, footer, }: DocumentPanelProps): import("react").JSX.Element;
20
+ export declare function DocumentPanel({ title, subtitle, meta, metaPlacement, statusBadge, state, emptyContent, loadingLabel, notices, children, footer, }: DocumentPanelProps): import("react").JSX.Element;
15
21
  //# sourceMappingURL=document-panel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"document-panel.d.ts","sourceRoot":"","sources":["../src/document-panel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMvC,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;IACrC,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAED,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,WAAW,EACX,KAAK,EACL,YAAY,EACZ,YAA0B,EAC1B,OAAO,EACP,QAAQ,EACR,MAAM,GACP,EAAE,kBAAkB,+BAqDpB"}
1
+ {"version":3,"file":"document-panel.d.ts","sourceRoot":"","sources":["../src/document-panel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMvC,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IACnC,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;IACrC,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAED,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,aAAuB,EACvB,WAAW,EACX,KAAK,EACL,YAAY,EACZ,YAA0B,EAC1B,OAAO,EACP,QAAQ,EACR,MAAM,GACP,EAAE,kBAAkB,+BAuEpB"}
@@ -2,7 +2,7 @@ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
2
  import { Divider } from '@lindaui/ui/divider';
3
3
  import { Skeleton } from '@lindaui/ui/skeleton';
4
4
 
5
- function DocumentPanel({ title, subtitle, meta, statusBadge, state, emptyContent, loadingLabel = "Cargando…", notices, children, footer }) {
5
+ function DocumentPanel({ title, subtitle, meta, metaPlacement = "block", statusBadge, state, emptyContent, loadingLabel = "Cargando…", notices, children, footer }) {
6
6
  return /*#__PURE__*/ jsxs("div", {
7
7
  className: "flex flex-col h-full",
8
8
  children: [
@@ -16,13 +16,31 @@ function DocumentPanel({ title, subtitle, meta, statusBadge, state, emptyContent
16
16
  className: "truncate text-lg font-bold leading-tight tracking-tight sm:text-xl",
17
17
  children: title
18
18
  }),
19
- subtitle && /*#__PURE__*/ jsx("p", {
20
- className: "mt-0.5 text-sm text-muted-foreground",
21
- children: subtitle
22
- }),
23
- meta && /*#__PURE__*/ jsx("p", {
24
- className: "mt-0.5 text-xs text-muted-foreground tabular-nums",
25
- children: meta
19
+ metaPlacement === "inline" ? (subtitle || meta) && /*#__PURE__*/ jsxs("div", {
20
+ className: "mt-0.5 flex flex-wrap items-center gap-x-2 text-sm text-muted-foreground",
21
+ children: [
22
+ subtitle && /*#__PURE__*/ jsx("span", {
23
+ children: subtitle
24
+ }),
25
+ meta && /*#__PURE__*/ jsxs("span", {
26
+ className: "text-xs tabular-nums",
27
+ children: [
28
+ subtitle ? "· " : "",
29
+ meta
30
+ ]
31
+ })
32
+ ]
33
+ }) : /*#__PURE__*/ jsxs(Fragment, {
34
+ children: [
35
+ subtitle && /*#__PURE__*/ jsx("p", {
36
+ className: "mt-0.5 text-sm text-muted-foreground",
37
+ children: subtitle
38
+ }),
39
+ meta && /*#__PURE__*/ jsx("p", {
40
+ className: "mt-0.5 text-xs text-muted-foreground tabular-nums",
41
+ children: meta
42
+ })
43
+ ]
26
44
  })
27
45
  ]
28
46
  }),
@@ -1 +1 @@
1
- {"version":3,"file":"field-array-form.d.ts","sourceRoot":"","sources":["../src/field-array-form.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAOvC,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;CAC/C;AAED,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,OAAO,EACP,aAA2B,EAC3B,SAAoB,EACpB,eAAe,EACf,YAAyB,EACzB,QAA4B,EAC5B,WAAgC,EAChC,cAAwB,EACxB,gBAA0B,EAC1B,SAAS,EACT,WAAuB,EACvB,OAAe,EACf,aAAa,GACd,EAAE,mBAAmB,+BAmHrB"}
1
+ {"version":3,"file":"field-array-form.d.ts","sourceRoot":"","sources":["../src/field-array-form.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAOvC,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;CAC/C;AAED,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,OAAO,EACP,aAA2B,EAC3B,SAAoB,EACpB,eAAe,EACf,YAAyB,EACzB,QAA4B,EAC5B,WAAgC,EAChC,cAAwB,EACxB,gBAA0B,EAC1B,SAAS,EACT,WAAuB,EACvB,OAAe,EACf,aAAa,GACd,EAAE,mBAAmB,+BAuHrB"}
@@ -70,27 +70,33 @@ function FieldArrayForm({ onSubmit, options, categoryLabel = "Categoría", nameL
70
70
  /*#__PURE__*/ jsx(Controller, {
71
71
  control: control,
72
72
  name: `entries.${index}.key`,
73
- render: ({ field: f })=>/*#__PURE__*/ jsx(Input, {
74
- placeholder: keyPlaceholder,
75
- "aria-label": `${keyPlaceholder} ${index + 1}`,
76
- value: f.value,
77
- onChange: f.onChange,
78
- onBlur: f.onBlur,
79
- name: f.name,
80
- className: "min-w-0 w-full"
73
+ render: ({ field: f })=>/*#__PURE__*/ jsx("div", {
74
+ className: "min-w-0 w-full",
75
+ children: /*#__PURE__*/ jsx(Input, {
76
+ placeholder: keyPlaceholder,
77
+ "aria-label": `${keyPlaceholder} ${index + 1}`,
78
+ value: f.value,
79
+ onChange: f.onChange,
80
+ onBlur: f.onBlur,
81
+ name: f.name,
82
+ fullWidth: true
83
+ })
81
84
  })
82
85
  }),
83
86
  /*#__PURE__*/ jsx(Controller, {
84
87
  control: control,
85
88
  name: `entries.${index}.value`,
86
- render: ({ field: f })=>/*#__PURE__*/ jsx(Input, {
87
- placeholder: valuePlaceholder,
88
- "aria-label": `${valuePlaceholder} ${index + 1}`,
89
- value: f.value,
90
- onChange: f.onChange,
91
- onBlur: f.onBlur,
92
- name: f.name,
93
- className: "min-w-0 w-full"
89
+ render: ({ field: f })=>/*#__PURE__*/ jsx("div", {
90
+ className: "min-w-0 w-full",
91
+ children: /*#__PURE__*/ jsx(Input, {
92
+ placeholder: valuePlaceholder,
93
+ "aria-label": `${valuePlaceholder} ${index + 1}`,
94
+ value: f.value,
95
+ onChange: f.onChange,
96
+ onBlur: f.onBlur,
97
+ name: f.name,
98
+ fullWidth: true
99
+ })
94
100
  })
95
101
  }),
96
102
  /*#__PURE__*/ jsx(Button, {
@@ -1 +1 @@
1
- {"version":3,"file":"list-pane.d.ts","sourceRoot":"","sources":["../src/list-pane.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAM9C,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAC7B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,wBAAgB,QAAQ,CAAC,EACvB,OAAO,EACP,YAAY,EACZ,cAAc,EACd,WAAgB,EAChB,cAAc,EACd,iBAA4B,EAC5B,WAAsB,EACtB,OAAe,EACf,KAAY,EACZ,OAAO,EACP,UAA8C,EAC9C,UAAyB,EACzB,OAAO,EACP,UAA6B,EAC7B,gBAAgB,EAChB,SAAS,EACT,QAAQ,GACT,EAAE,aAAa,qBAgGf"}
1
+ {"version":3,"file":"list-pane.d.ts","sourceRoot":"","sources":["../src/list-pane.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAM9C,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAC7B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,wBAAgB,QAAQ,CAAC,EACvB,OAAO,EACP,YAAY,EACZ,cAAc,EACd,WAAgB,EAChB,cAAc,EACd,iBAA4B,EAC5B,WAAsB,EACtB,OAAe,EACf,KAAY,EACZ,OAAO,EACP,UAA8C,EAC9C,UAAyB,EACzB,OAAO,EACP,UAA6B,EAC7B,gBAAgB,EAChB,SAAS,EACT,QAAQ,GACT,EAAE,aAAa,qBA2Ff"}
package/dist/list-pane.js CHANGED
@@ -1,9 +1,9 @@
1
1
  'use client';
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
3
  import React from 'react';
4
- import { Search, AlertCircle, RotateCcw, FileText } from 'lucide-react';
4
+ import { AlertCircle, RotateCcw, FileText } from 'lucide-react';
5
5
  import { Button } from '@lindaui/ui/button';
6
- import { Input } from '@lindaui/ui/input';
6
+ import { SearchField } from '@lindaui/ui/search-field';
7
7
  import { Skeleton } from '@lindaui/ui/skeleton';
8
8
 
9
9
  function ListPane({ filters, activeFilter, onFilterChange, searchValue = "", onSearchChange, searchPlaceholder = "Buscar", searchLabel = "Buscar", loading = false, error = null, onRetry, errorTitle = "No se pudieron cargar los datos", retryLabel = "Reintentar", isEmpty, emptyTitle = "Sin resultados", emptyDescription, emptyIcon, children }) {
@@ -30,21 +30,11 @@ function ListPane({ filters, activeFilter, onFilterChange, searchValue = "", onS
30
30
  ]
31
31
  }, f.key))
32
32
  }),
33
- /*#__PURE__*/ jsxs("div", {
34
- className: "relative",
35
- children: [
36
- /*#__PURE__*/ jsx(Search, {
37
- className: "pointer-events-none absolute left-2.5 top-1/2 size-4 -translate-y-1/2 text-muted-foreground z-10"
38
- }),
39
- /*#__PURE__*/ jsx(Input, {
40
- type: "search",
41
- value: searchValue,
42
- onChange: onSearchChange ?? (()=>{}),
43
- placeholder: searchPlaceholder,
44
- "aria-label": searchLabel,
45
- className: "h-9 pl-8"
46
- })
47
- ]
33
+ /*#__PURE__*/ jsx(SearchField, {
34
+ value: searchValue,
35
+ onChange: onSearchChange ?? (()=>{}),
36
+ placeholder: searchPlaceholder,
37
+ "aria-label": searchLabel
48
38
  })
49
39
  ]
50
40
  }),
@@ -8,6 +8,14 @@ export interface RecordingOverlayProps {
8
8
  stopLabel?: string;
9
9
  errorTitle?: string;
10
10
  backLabel?: string;
11
+ /**
12
+ * `overlay` (default) covers its container on a fixed dark surface (`viewer-*`
13
+ * tokens) — for use over media. `inline` drops the absolute positioning and the
14
+ * forced dark background, filling its container and inheriting the panel's
15
+ * surface; its text is token-driven so it stays legible on a light or dark
16
+ * panel. Content is otherwise identical.
17
+ */
18
+ variant?: "overlay" | "inline";
11
19
  }
12
- export declare function RecordingOverlay({ label, description, elapsed, error, onStop, onCancel, stopLabel, errorTitle, backLabel, }: RecordingOverlayProps): import("react").JSX.Element;
20
+ export declare function RecordingOverlay({ label, description, elapsed, error, onStop, onCancel, stopLabel, errorTitle, backLabel, variant, }: RecordingOverlayProps): import("react").JSX.Element;
13
21
  //# sourceMappingURL=recording-overlay.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"recording-overlay.d.ts","sourceRoot":"","sources":["../src/recording-overlay.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAmCD,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,WAAW,EACX,OAAO,EACP,KAAK,EACL,MAAM,EACN,QAAQ,EACR,SAAqB,EACrB,UAA8C,EAC9C,SAAoB,GACrB,EAAE,qBAAqB,+BAiEvB"}
1
+ {"version":3,"file":"recording-overlay.d.ts","sourceRoot":"","sources":["../src/recording-overlay.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;CAChC;AAsDD,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,WAAW,EACX,OAAO,EACP,KAAK,EACL,MAAM,EACN,QAAQ,EACR,SAAqB,EACrB,UAA8C,EAC9C,SAAoB,EACpB,OAAmB,GACpB,EAAE,qBAAqB,+BAuEvB"}
@@ -2,6 +2,23 @@
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
3
  import { AlertCircle, Mic } from 'lucide-react';
4
4
 
5
+ /** Per-variant surface/text tokens so both read on their respective background. */ function variantTokens(variant) {
6
+ if (variant === "inline") {
7
+ return {
8
+ root: "ti-fade flex h-full flex-col items-center justify-center gap-5 p-6 sm:gap-7",
9
+ text: "text-foreground",
10
+ muted: "text-muted-foreground",
11
+ // solid action button on an adaptive panel
12
+ solid: "bg-foreground text-background"
13
+ };
14
+ }
15
+ return {
16
+ root: "ti-fade absolute inset-0 z-[60] flex flex-col items-center justify-center gap-5 bg-viewer-bg p-8 text-viewer-foreground sm:gap-7",
17
+ text: "text-viewer-foreground",
18
+ muted: "text-viewer-foreground/70",
19
+ solid: "bg-white text-[oklch(0.16_0.02_240)]"
20
+ };
21
+ }
5
22
  function fmtTime(sec) {
6
23
  const m = Math.floor(sec / 60);
7
24
  const s = sec % 60;
@@ -30,10 +47,11 @@ function Waveform() {
30
47
  })
31
48
  });
32
49
  }
33
- function RecordingOverlay({ label, description, elapsed, error, onStop, onCancel, stopLabel = "Detener", errorTitle = "No se pudo acceder al micrófono", backLabel = "Volver" }) {
50
+ function RecordingOverlay({ label, description, elapsed, error, onStop, onCancel, stopLabel = "Detener", errorTitle = "No se pudo acceder al micrófono", backLabel = "Volver", variant = "overlay" }) {
51
+ const t = variantTokens(variant);
34
52
  if (error) {
35
53
  return /*#__PURE__*/ jsxs("div", {
36
- className: "absolute inset-0 z-[60] flex flex-col items-center justify-center gap-5 bg-[oklch(0.16_0.02_240)] p-8 text-center",
54
+ className: `${t.root} text-center`,
37
55
  children: [
38
56
  /*#__PURE__*/ jsx("div", {
39
57
  className: "grid h-16 w-16 place-items-center rounded-full bg-destructive/15 text-destructive",
@@ -45,11 +63,11 @@ function RecordingOverlay({ label, description, elapsed, error, onStop, onCancel
45
63
  className: "max-w-[280px]",
46
64
  children: [
47
65
  /*#__PURE__*/ jsx("p", {
48
- className: "text-base font-semibold text-white",
66
+ className: `text-base font-semibold ${t.text}`,
49
67
  children: errorTitle
50
68
  }),
51
69
  /*#__PURE__*/ jsx("p", {
52
- className: "mt-2 text-sm text-white/65",
70
+ className: `mt-2 text-sm ${t.muted}`,
53
71
  children: error
54
72
  })
55
73
  ]
@@ -57,14 +75,14 @@ function RecordingOverlay({ label, description, elapsed, error, onStop, onCancel
57
75
  /*#__PURE__*/ jsx("button", {
58
76
  type: "button",
59
77
  onClick: onCancel,
60
- className: "h-12 rounded-2xl bg-white px-6 text-[15px] font-semibold text-[oklch(0.16_0.02_240)]",
78
+ className: `h-12 rounded-2xl px-6 text-[15px] font-semibold ${t.solid}`,
61
79
  children: backLabel
62
80
  })
63
81
  ]
64
82
  });
65
83
  }
66
84
  return /*#__PURE__*/ jsxs("div", {
67
- className: "absolute inset-0 z-[60] flex flex-col items-center justify-center gap-5 bg-[oklch(0.16_0.02_240)] text-[oklch(0.9_0.01_220)] sm:gap-7",
85
+ className: t.root,
68
86
  children: [
69
87
  /*#__PURE__*/ jsxs("div", {
70
88
  className: "relative grid h-[132px] w-[132px] place-items-center sm:h-[168px] sm:w-[168px]",
@@ -96,26 +114,26 @@ function RecordingOverlay({ label, description, elapsed, error, onStop, onCancel
96
114
  className: "ti-rec-blink h-2.5 w-2.5 rounded-full bg-destructive"
97
115
  }),
98
116
  /*#__PURE__*/ jsx("span", {
99
- className: "text-sm font-semibold tracking-wide text-[oklch(0.72_0.16_18)]",
117
+ className: "text-sm font-semibold tracking-wide text-destructive",
100
118
  children: label
101
119
  })
102
120
  ]
103
121
  }),
104
122
  /*#__PURE__*/ jsx("div", {
105
- className: "mt-2 font-mono text-4xl font-medium tabular-nums text-white sm:text-[46px]",
123
+ className: `mt-2 font-mono text-4xl font-medium tabular-nums ${t.text} sm:text-[46px]`,
106
124
  children: fmtTime(elapsed)
107
125
  })
108
126
  ]
109
127
  }),
110
128
  /*#__PURE__*/ jsx(Waveform, {}),
111
129
  description && /*#__PURE__*/ jsx("p", {
112
- className: "max-w-[270px] text-center text-sm leading-relaxed text-white/70",
130
+ className: `max-w-[270px] text-center text-sm leading-relaxed ${t.muted}`,
113
131
  children: description
114
132
  }),
115
133
  /*#__PURE__*/ jsx("button", {
116
134
  type: "button",
117
135
  onClick: onStop,
118
- className: "mt-1.5 grid h-16 w-16 place-items-center rounded-full bg-white shadow-[0_6px_20px_-4px_rgba(0,0,0,0.5)]",
136
+ className: `mt-1.5 grid h-16 w-16 place-items-center rounded-full ${t.solid} shadow-[0_6px_20px_-4px_rgba(0,0,0,0.5)]`,
119
137
  "aria-label": stopLabel,
120
138
  children: /*#__PURE__*/ jsx("span", {
121
139
  className: "h-6 w-6 rounded-md bg-destructive"
@@ -3,7 +3,14 @@ export interface SplitWorkspaceProps {
3
3
  media: ReactNode;
4
4
  panel: ReactNode;
5
5
  overlay?: ReactNode;
6
+ /**
7
+ * Custom back control (escape hatch). Wins over `backHref`/`backLabel`.
8
+ * Prefer `backHref`/`backLabel` — the default renders a legible pill over
9
+ * any media without the consumer having to guarantee contrast.
10
+ */
6
11
  back?: ReactNode;
12
+ backHref?: string;
13
+ backLabel?: ReactNode;
7
14
  }
8
- export declare function SplitWorkspace({ media, panel, overlay, back, }: SplitWorkspaceProps): import("react").JSX.Element;
15
+ export declare function SplitWorkspace({ media, panel, overlay, back, backHref, backLabel, }: SplitWorkspaceProps): import("react").JSX.Element;
9
16
  //# sourceMappingURL=split-workspace.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"split-workspace.d.ts","sourceRoot":"","sources":["../src/split-workspace.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAED,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,KAAK,EACL,OAAO,EACP,IAAI,GACL,EAAE,mBAAmB,+BAuCrB"}
1
+ {"version":3,"file":"split-workspace.d.ts","sourceRoot":"","sources":["../src/split-workspace.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB;;;;OAIG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,KAAK,EACL,OAAO,EACP,IAAI,EACJ,QAAQ,EACR,SAAoB,GACrB,EAAE,mBAAmB,+BA8CrB"}
@@ -1,9 +1,14 @@
1
1
  'use client';
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
3
  import { useIsDesktop } from '@lindaui/ui/use-media-query';
4
+ import { WorkspaceBackButton } from './workspace-back-button.js';
4
5
 
5
- function SplitWorkspace({ media, panel, overlay, back }) {
6
+ function SplitWorkspace({ media, panel, overlay, back, backHref, backLabel = "Volver" }) {
6
7
  const isDesktop = useIsDesktop();
8
+ const backNode = back ?? (backHref != null ? /*#__PURE__*/ jsx(WorkspaceBackButton, {
9
+ href: backHref,
10
+ label: backLabel
11
+ }) : null);
7
12
  return /*#__PURE__*/ jsxs("div", {
8
13
  className: "relative flex-1 min-h-0 overflow-hidden",
9
14
  children: [
@@ -35,9 +40,12 @@ function SplitWorkspace({ media, panel, overlay, back }) {
35
40
  })
36
41
  ]
37
42
  }),
38
- back && /*#__PURE__*/ jsx("div", {
39
- className: "absolute left-3.5 top-3 z-40",
40
- children: back
43
+ backNode && /*#__PURE__*/ jsx("div", {
44
+ className: "pointer-events-none absolute inset-x-0 top-0 z-40 bg-linear-to-b from-black/25 to-transparent p-3 pt-[max(0.75rem,env(safe-area-inset-top))] pl-[max(0.875rem,env(safe-area-inset-left))]",
45
+ children: /*#__PURE__*/ jsx("div", {
46
+ className: "pointer-events-auto inline-block",
47
+ children: backNode
48
+ })
41
49
  }),
42
50
  overlay && /*#__PURE__*/ jsx("div", {
43
51
  className: "absolute inset-0 z-[60]",
@@ -0,0 +1,15 @@
1
+ import type { ReactNode } from "react";
2
+ /**
3
+ * Legible back pill for media overlays. `SplitWorkspace` renders it by default
4
+ * from `backHref`/`backLabel` so a consumer never has to hand-build a readable
5
+ * control over dark/bright media. Token-driven surface (`bg-secondary` +
6
+ * `border-border`) so it reads on any background.
7
+ */
8
+ export interface WorkspaceBackButtonProps {
9
+ href: string;
10
+ label: ReactNode;
11
+ /** Leading icon; defaults to a chevron. Rendered `aria-hidden` — the label names the link. */
12
+ icon?: ReactNode;
13
+ }
14
+ export declare function WorkspaceBackButton({ href, label, icon, }: WorkspaceBackButtonProps): import("react").JSX.Element;
15
+ //# sourceMappingURL=workspace-back-button.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-back-button.d.ts","sourceRoot":"","sources":["../src/workspace-back-button.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC;;;;;GAKG;AACH,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,SAAS,CAAC;IACjB,8FAA8F;IAC9F,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,KAAK,EACL,IAA4C,GAC7C,EAAE,wBAAwB,+BAU1B"}
@@ -0,0 +1,19 @@
1
+ 'use client';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { ChevronLeft } from 'lucide-react';
4
+
5
+ function WorkspaceBackButton({ href, label, icon = /*#__PURE__*/ jsx(ChevronLeft, {
6
+ size: 18,
7
+ "aria-hidden": true
8
+ }) }) {
9
+ return /*#__PURE__*/ jsxs("a", {
10
+ href: href,
11
+ className: "inline-flex h-9 items-center gap-1 rounded-full border border-border bg-secondary pl-2 pr-3 text-sm font-medium text-secondary-foreground shadow-sm transition-colors hover:bg-border",
12
+ children: [
13
+ icon,
14
+ label
15
+ ]
16
+ });
17
+ }
18
+
19
+ export { WorkspaceBackButton };
package/llms.txt ADDED
@@ -0,0 +1,123 @@
1
+ # @lindaui/blocks
2
+
3
+ > Composed UI sections built on **@lindaui/ui** primitives — auth forms, master-detail,
4
+ > lists, dialogs, media viewers, and charts. Importable npm package (NOT a copy-paste
5
+ > registry). Each block has its OWN flat API and does not leak HeroUI/RAC types.
6
+ > This file is for coding agents: every entry, import path, and public API.
7
+
8
+ All blocks are generic, controlled/mobile-first, and contain zero data-fetching, no
9
+ `next`, no `sonner`. Controlled blocks receive state + handlers from you. For the exact
10
+ type of any export, read `node_modules/@lindaui/blocks/dist/<name>.d.ts`.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ pnpm add @lindaui/blocks @lindaui/ui @lindaui/tokens
16
+ ```
17
+
18
+ Peers you provide: `react >=19`, `react-dom >=19`, `react-hook-form >=7`, `recharts >=3`
19
+ (only for chart blocks), plus `@lindaui/ui` and `@lindaui/tokens`. Import the CSS once
20
+ at app root: `import "@lindaui/tokens/css";`.
21
+
22
+ ## Import convention
23
+
24
+ One block per import path: `@lindaui/blocks/<name>`.
25
+
26
+ ---
27
+
28
+ ## Auth & forms
29
+
30
+ ### @lindaui/blocks/login-form
31
+ `<LoginForm onSubmit={(values)=>...} title emailLabel passwordLabel submitLabel loading
32
+ error bare />` — RHF handled internally; `onSubmit({email,password})`. `bare` drops the
33
+ Card/title for embedding inside a shell (e.g. AuthLayout).
34
+
35
+ ### @lindaui/blocks/auth-layout
36
+ Split-screen auth shell. Slots: `logo`, `compactLogo`, `brandHeadline`,
37
+ `brandDescription`, `brandFeatures: {icon,label}[]`, `brandFooter`, `title`,
38
+ `description`, `footer`, and `children` (the form). Desktop shows a dark brand panel;
39
+ without `brandHeadline`/`brandFeatures` it collapses to a single centered form column.
40
+
41
+ ### @lindaui/blocks/auth-provider
42
+ Context `{ user }` + `useAuth()` hook. Wrap your app; read the current user anywhere.
43
+
44
+ ### @lindaui/blocks/field-array-form
45
+ A select + repeatable rows form via RHF `useFieldArray`.
46
+
47
+ ## Lists & master-detail (controlled)
48
+
49
+ ### @lindaui/blocks/list-item
50
+ Selectable row. Controlled selected state + `onSelect`.
51
+
52
+ ### @lindaui/blocks/list-pane
53
+ Filterable/searchable list shell with loading/empty/error states. Controlled:
54
+ `filters: {key,label,count?}[]`, `activeFilter`, `onFilterChange`, `searchValue`,
55
+ `onSearchChange`, `loading`, `error`, `onRetry`, `isEmpty`, empty/error slots. Items go
56
+ in via `children`. Does NOT filter for you — filter your data (use `@lindaui/ui/search`).
57
+
58
+ ### @lindaui/blocks/detail-panel
59
+ Master-detail right side: empty state + header + meta card + CTA. Controlled.
60
+
61
+ ### @lindaui/blocks/split-workspace
62
+ Responsive split `media | panel` (controlled). Props: `media`, `panel`, `overlay?`,
63
+ `backHref?`, `backLabel?` (default `"Volver"`), `back?`. Two-column on desktop,
64
+ stacked on mobile (uses `useIsDesktop`). Pass `backHref`/`backLabel` to get a
65
+ legible default back pill over any media (readability scrim + safe-area, styled
66
+ with `WorkspaceBackButton`); `back` is an escape hatch that wins over the pair.
67
+
68
+ ### @lindaui/blocks/workspace-back-button
69
+ `<WorkspaceBackButton href label icon? />` — legible back pill (`<a>`, token-driven
70
+ `bg-secondary`/`border-border`) that reads over dark or bright media. Used by
71
+ `split-workspace`'s default; usable standalone. `icon` defaults to a chevron.
72
+
73
+ ## Dialogs & overlays
74
+
75
+ ### @lindaui/blocks/confirm-dialog
76
+ `<ConfirmDialog open onOpenChange onConfirm title description confirmLabel cancelLabel
77
+ icon destructive identity={{name}} />` — `destructive` → red confirm button + red icon
78
+ box; `identity` → actor chip under the description.
79
+
80
+ ### @lindaui/blocks/image-viewer
81
+ Full-bleed media viewer with zoom (uses `viewer-*` tokens).
82
+
83
+ ### @lindaui/blocks/recording-overlay
84
+ Voice-recording UI: mic / timer / waveform / stop / cancel / error. Props include
85
+ `variant?: "overlay" | "inline"` (default `overlay`). `overlay` covers its container
86
+ on a fixed dark `viewer-*` surface (for use over media); `inline` drops the absolute
87
+ positioning + forced dark bg, fills its container and inherits the panel surface with
88
+ token-driven (legible) text. Content is identical across variants.
89
+
90
+ ## Documents
91
+
92
+ ### @lindaui/blocks/document-panel
93
+ Header + per-state body + footer. Controlled. Props: `title`, `subtitle?`, `meta?`,
94
+ `metaPlacement?: "block"|"inline"` (default `block`), `statusBadge?`, `state`,
95
+ `emptyContent?`, `loadingLabel?`, `notices?`, `footer?`. `metaPlacement="inline"`
96
+ flows subtitle + meta on one wrapping row (`subtitle · meta`) instead of stacking.
97
+
98
+ ### @lindaui/blocks/document-reader
99
+ Sectioned text reader.
100
+
101
+ ## Charts (over @lindaui/ui/chart + recharts)
102
+
103
+ All chart blocks share a flat API:
104
+ `data: Record<string,string|number>[]`, `config: ChartConfig` (serie → {label,color}),
105
+ `categoryKey: string`, `series: string[]` (or `valueKey`/`nameKey` for pie/radial),
106
+ `variant?`, `className?`. Series color comes from `var(--color-<key>)` that
107
+ `ChartContainer` injects from `config`; dark mode is free.
108
+
109
+ ```tsx
110
+ import { AreaChart } from "@lindaui/blocks/area-chart";
111
+ const config = { sales: { label: "Sales", color: "var(--chart-1)" } };
112
+ <AreaChart data={data} config={config} categoryKey="month" series={["sales"]} />
113
+ ```
114
+
115
+ - `@lindaui/blocks/area-chart` — `variant: "default"|"stacked"|"gradient"`
116
+ - `@lindaui/blocks/bar-chart`
117
+ - `@lindaui/blocks/line-chart`
118
+ - `@lindaui/blocks/pie-chart`
119
+ - `@lindaui/blocks/radar-chart`
120
+ - `@lindaui/blocks/radial-chart`
121
+ - `@lindaui/blocks/stat-card` — KPI card: label/value/delta/sparkline
122
+ - `@lindaui/blocks/chart-card` — Card shell wrapping a chart
123
+ - `@lindaui/blocks/stats-grid` — responsive grid of KPI stat-cards
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@lindaui/blocks",
3
- "version": "0.0.2",
3
+ "version": "0.1.0",
4
4
  "description": "Composed UI sections (auth, lists, master-detail, charts) built on @lindaui/ui — importable npm package, not a copy-paste registry.",
5
5
  "author": "Franco Carballar <francocarballar@gmail.com>",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "git+https://github.com/francocarballar/ts-design-system.git",
9
+ "url": "git+https://github.com/francocarballar/lindaui.git",
10
10
  "directory": "packages/blocks"
11
11
  },
12
12
  "publishConfig": {
@@ -102,12 +102,17 @@
102
102
  "./stats-grid": {
103
103
  "types": "./dist/stats-grid.d.ts",
104
104
  "import": "./dist/stats-grid.js"
105
+ },
106
+ "./workspace-back-button": {
107
+ "types": "./dist/workspace-back-button.d.ts",
108
+ "import": "./dist/workspace-back-button.js"
105
109
  }
106
110
  },
107
111
  "files": [
108
112
  "dist",
109
113
  "LICENSE",
110
- "README.md"
114
+ "README.md",
115
+ "llms.txt"
111
116
  ],
112
117
  "peerDependencies": {
113
118
  "react": ">=19",
@@ -137,8 +142,8 @@
137
142
  "zod": "^3.23.0",
138
143
  "@hookform/resolvers": "^3.9.0",
139
144
  "typescript": "^6.0.3",
140
- "@lindaui/ui": "0.0.2",
141
- "@lindaui/tokens": "0.0.2"
145
+ "@lindaui/ui": "0.1.0",
146
+ "@lindaui/tokens": "0.2.1"
142
147
  },
143
148
  "scripts": {
144
149
  "build": "bunchee --no-dts && tsc -p tsconfig.build.json",