tempest-react-sdk 0.24.0 → 0.25.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.
Files changed (106) hide show
  1. package/README.md +11 -3
  2. package/dist/charts/AreaChart.cjs +1 -1
  3. package/dist/charts/AreaChart.cjs.map +1 -1
  4. package/dist/charts/AreaChart.js +7 -7
  5. package/dist/charts/AreaChart.js.map +1 -1
  6. package/dist/charts/BarChart.cjs +1 -1
  7. package/dist/charts/BarChart.cjs.map +1 -1
  8. package/dist/charts/BarChart.js +7 -7
  9. package/dist/charts/BarChart.js.map +1 -1
  10. package/dist/charts/LineChart.cjs +1 -1
  11. package/dist/charts/LineChart.cjs.map +1 -1
  12. package/dist/charts/LineChart.js +6 -6
  13. package/dist/charts/LineChart.js.map +1 -1
  14. package/dist/charts/PieChart.cjs +1 -1
  15. package/dist/charts/PieChart.cjs.map +1 -1
  16. package/dist/charts/PieChart.js +6 -6
  17. package/dist/charts/PieChart.js.map +1 -1
  18. package/dist/charts/RadarChart.cjs +1 -1
  19. package/dist/charts/RadarChart.cjs.map +1 -1
  20. package/dist/charts/RadarChart.js +6 -6
  21. package/dist/charts/RadarChart.js.map +1 -1
  22. package/dist/charts/palette.cjs +1 -1
  23. package/dist/charts/palette.cjs.map +1 -1
  24. package/dist/charts/palette.js +19 -2
  25. package/dist/charts/palette.js.map +1 -1
  26. package/dist/charts/use-chart-colors.cjs +2 -0
  27. package/dist/charts/use-chart-colors.cjs.map +1 -0
  28. package/dist/charts/use-chart-colors.js +29 -0
  29. package/dist/charts/use-chart-colors.js.map +1 -0
  30. package/dist/charts.cjs +1 -1
  31. package/dist/charts.d.ts +59 -10
  32. package/dist/charts.js +8 -7
  33. package/dist/components/AvatarGroup/AvatarGroup.cjs +2 -0
  34. package/dist/components/AvatarGroup/AvatarGroup.cjs.map +1 -0
  35. package/dist/components/AvatarGroup/AvatarGroup.js +39 -0
  36. package/dist/components/AvatarGroup/AvatarGroup.js.map +1 -0
  37. package/dist/components/AvatarGroup/AvatarGroup.module.cjs +2 -0
  38. package/dist/components/AvatarGroup/AvatarGroup.module.cjs.map +1 -0
  39. package/dist/components/AvatarGroup/AvatarGroup.module.js +16 -0
  40. package/dist/components/AvatarGroup/AvatarGroup.module.js.map +1 -0
  41. package/dist/components/Lightbox/Lightbox.cjs +2 -0
  42. package/dist/components/Lightbox/Lightbox.cjs.map +1 -0
  43. package/dist/components/Lightbox/Lightbox.js +148 -0
  44. package/dist/components/Lightbox/Lightbox.js.map +1 -0
  45. package/dist/components/Lightbox/Lightbox.module.cjs +2 -0
  46. package/dist/components/Lightbox/Lightbox.module.cjs.map +1 -0
  47. package/dist/components/Lightbox/Lightbox.module.js +21 -0
  48. package/dist/components/Lightbox/Lightbox.module.js.map +1 -0
  49. package/dist/components/SignaturePad/SignaturePad.cjs +2 -0
  50. package/dist/components/SignaturePad/SignaturePad.cjs.map +1 -0
  51. package/dist/components/SignaturePad/SignaturePad.js +115 -0
  52. package/dist/components/SignaturePad/SignaturePad.js.map +1 -0
  53. package/dist/components/SignaturePad/SignaturePad.module.cjs +2 -0
  54. package/dist/components/SignaturePad/SignaturePad.module.cjs.map +1 -0
  55. package/dist/components/SignaturePad/SignaturePad.module.js +11 -0
  56. package/dist/components/SignaturePad/SignaturePad.module.js.map +1 -0
  57. package/dist/components/Stepper/Stepper.cjs +1 -1
  58. package/dist/components/Stepper/Stepper.cjs.map +1 -1
  59. package/dist/components/Stepper/Stepper.js +43 -19
  60. package/dist/components/Stepper/Stepper.js.map +1 -1
  61. package/dist/components/Stepper/Stepper.module.cjs +1 -1
  62. package/dist/components/Stepper/Stepper.module.cjs.map +1 -1
  63. package/dist/components/Stepper/Stepper.module.js +6 -3
  64. package/dist/components/Stepper/Stepper.module.js.map +1 -1
  65. package/dist/components/TreeView/TreeView.cjs +2 -0
  66. package/dist/components/TreeView/TreeView.cjs.map +1 -0
  67. package/dist/components/TreeView/TreeView.js +124 -0
  68. package/dist/components/TreeView/TreeView.js.map +1 -0
  69. package/dist/components/TreeView/TreeView.module.cjs +2 -0
  70. package/dist/components/TreeView/TreeView.module.cjs.map +1 -0
  71. package/dist/components/TreeView/TreeView.module.js +17 -0
  72. package/dist/components/TreeView/TreeView.module.js.map +1 -0
  73. package/dist/components/Wizard/Wizard.cjs +2 -0
  74. package/dist/components/Wizard/Wizard.cjs.map +1 -0
  75. package/dist/components/Wizard/Wizard.js +111 -0
  76. package/dist/components/Wizard/Wizard.js.map +1 -0
  77. package/dist/components/Wizard/Wizard.module.cjs +2 -0
  78. package/dist/components/Wizard/Wizard.module.cjs.map +1 -0
  79. package/dist/components/Wizard/Wizard.module.js +10 -0
  80. package/dist/components/Wizard/Wizard.module.js.map +1 -0
  81. package/dist/offline/create-offline-sync.cjs +1 -1
  82. package/dist/offline/create-offline-sync.cjs.map +1 -1
  83. package/dist/offline/create-offline-sync.js +8 -2
  84. package/dist/offline/create-offline-sync.js.map +1 -1
  85. package/dist/styles.css +1 -1
  86. package/dist/tempest-react-sdk.cjs +1 -1
  87. package/dist/tempest-react-sdk.d.ts +605 -2
  88. package/dist/tempest-react-sdk.js +255 -246
  89. package/dist/theme/apply-theme.cjs +2 -0
  90. package/dist/theme/apply-theme.cjs.map +1 -0
  91. package/dist/theme/apply-theme.js +24 -0
  92. package/dist/theme/apply-theme.js.map +1 -0
  93. package/dist/theme/color.cjs +2 -0
  94. package/dist/theme/color.cjs.map +1 -0
  95. package/dist/theme/color.js +130 -0
  96. package/dist/theme/color.js.map +1 -0
  97. package/dist/theme/create-theme.cjs +5 -0
  98. package/dist/theme/create-theme.cjs.map +1 -0
  99. package/dist/theme/create-theme.js +135 -0
  100. package/dist/theme/create-theme.js.map +1 -0
  101. package/dist/theme/theme-presets.cjs +2 -0
  102. package/dist/theme/theme-presets.cjs.map +1 -0
  103. package/dist/theme/theme-presets.js +82 -0
  104. package/dist/theme/theme-presets.js.map +1 -0
  105. package/dist/utilities.css +432 -0
  106. package/package.json +4 -3
package/dist/charts.d.ts CHANGED
@@ -4,7 +4,7 @@ import { JSX } from 'react';
4
4
  * AreaChart — themed wrapper over recharts `AreaChart`.
5
5
  *
6
6
  * Plots one filled area per entry in `categories`, colored from `colors`
7
- * (cycling {@link DEFAULT_CHART_COLORS} by default). When `stack` is set, all
7
+ * (cycling the `--tempest-chart-*` theme tokens by default). When `stack` is set, all
8
8
  * areas share a stackId. When `width` is provided the chart renders at that
9
9
  * fixed size without a ResponsiveContainer; otherwise it fills its parent.
10
10
  */
@@ -14,7 +14,7 @@ export declare function AreaChart({ data, index, categories, colors, height, wid
14
14
  * BarChart — themed wrapper over recharts `BarChart`.
15
15
  *
16
16
  * Plots one bar series per entry in `categories`, colored from `colors`
17
- * (cycling {@link DEFAULT_CHART_COLORS} by default). When `stack` is set, all
17
+ * (cycling the `--tempest-chart-*` theme tokens by default). When `stack` is set, all
18
18
  * bars share a stackId. When `width` is provided the chart renders at that
19
19
  * fixed size without a ResponsiveContainer; otherwise it fills its parent.
20
20
  */
@@ -57,6 +57,9 @@ export declare interface CartesianChartProps {
57
57
  className?: string;
58
58
  }
59
59
 
60
+ /** How many `--tempest-chart-N` tokens the SDK ships. */
61
+ export declare const CHART_COLOR_TOKEN_COUNT = 8;
62
+
60
63
  /**
61
64
  * Tabular data consumed by every chart: an array of rows, where each row maps a
62
65
  * column key to a string (label) or number (value).
@@ -64,12 +67,13 @@ export declare interface CartesianChartProps {
64
67
  export declare type ChartData = Array<Record<string, string | number>>;
65
68
 
66
69
  /**
67
- * Default color palette for Tempest charts.
70
+ * Fallback palette: six visually distinct hex colors (blue, green, amber,
71
+ * violet, pink, cyan) in cycle order.
68
72
  *
69
- * A set of six visually distinct hex colors (blue, green, amber, violet, pink,
70
- * cyan) used to color chart series in cycle order. Exported so callers can
71
- * import and theme their own charts, or pass a custom `colors` array to any
72
- * chart component to override it.
73
+ * Used when the `--tempest-chart-*` tokens cannot be read no stylesheet
74
+ * imported, a non-browser environment (tests, build scripts), or a host page that
75
+ * dropped the tokens. Exported so callers can start from it and pass a tweaked
76
+ * array to any chart's `colors` prop.
73
77
  */
74
78
  export declare const DEFAULT_CHART_COLORS: string[];
75
79
 
@@ -77,7 +81,7 @@ export declare const DEFAULT_CHART_COLORS: string[];
77
81
  * LineChart — themed wrapper over recharts `LineChart`.
78
82
  *
79
83
  * Plots one line per entry in `categories`, colored from `colors` (cycling
80
- * {@link DEFAULT_CHART_COLORS} by default). `stack` is accepted for API parity
84
+ * the `--tempest-chart-*` theme tokens by default). `stack` is accepted for API parity
81
85
  * but does not visually stack lines. When `width` is provided the chart renders
82
86
  * at that fixed size without a ResponsiveContainer; otherwise it fills its
83
87
  * parent.
@@ -89,7 +93,7 @@ export declare function LineChart({ data, index, categories, colors, height, wid
89
93
  *
90
94
  * Renders one slice per row, reading the numeric value from `category` and the
91
95
  * label from `index`. Each slice is colored from `colors` (cycling
92
- * {@link DEFAULT_CHART_COLORS} by default). When `donut` is set the pie gets a
96
+ * the `--tempest-chart-*` theme tokens by default). When `donut` is set the pie gets a
93
97
  * non-zero inner radius. When `width` is provided the chart renders at that
94
98
  * fixed size without a ResponsiveContainer; otherwise it fills its parent.
95
99
  */
@@ -130,10 +134,55 @@ export declare interface PieChartProps {
130
134
  * RadarChart — themed wrapper over recharts `RadarChart`.
131
135
  *
132
136
  * Plots one radar polygon per entry in `categories`, colored from `colors`
133
- * (cycling {@link DEFAULT_CHART_COLORS} by default). `index` drives the angle
137
+ * (cycling the `--tempest-chart-*` theme tokens by default). `index` drives the angle
134
138
  * axis. When `width` is provided the chart renders at that fixed size without a
135
139
  * ResponsiveContainer; otherwise it fills its parent.
136
140
  */
137
141
  export declare function RadarChart({ data, index, categories, colors, height, width, showLegend, showTooltip, valueFormatter, className, }: CartesianChartProps): JSX.Element;
138
142
 
143
+ /**
144
+ * Read one piece of chart chrome (`--tempest-chart-grid` / `--tempest-chart-axis`).
145
+ *
146
+ * @param part - Which chrome token to read.
147
+ * @param element - Element to resolve against. Default `<html>`.
148
+ * @param fallback - Value returned when the token is unset.
149
+ * @returns The resolved color, or `fallback`.
150
+ */
151
+ export declare function resolveChartChrome(part: "grid" | "axis", element?: Element | null, fallback?: string): string;
152
+
153
+ /**
154
+ * Read `--tempest-chart-1` … `--tempest-chart-8` into a color array.
155
+ *
156
+ * Stops at the first unset token, so an app that overrides only four series gets
157
+ * exactly those four cycled instead of a half-themed tail. Returns
158
+ * {@link DEFAULT_CHART_COLORS} when nothing is resolvable.
159
+ *
160
+ * @param element - Element to resolve the tokens against. Default `<html>`; pass
161
+ * a subtree root when a section carries a scoped theme.
162
+ * @returns Literal color strings in cycle order.
163
+ */
164
+ export declare function resolveChartColors(element?: Element | null): string[];
165
+
166
+ /**
167
+ * Theme-aware series colors for a chart.
168
+ *
169
+ * Resolves the `--tempest-chart-*` tokens on mount and again whenever the theme
170
+ * attribute flips, so switching to dark re-colors the series instead of leaving
171
+ * light-theme colors on a dark canvas. Passing `explicit` short-circuits the
172
+ * whole thing — an explicit `colors` prop always wins and no observer is set up.
173
+ *
174
+ * @param explicit - Colors passed by the caller. When present, returned as-is.
175
+ * @param element - Element to resolve tokens against. Default `<html>`.
176
+ * @returns Colors in cycle order.
177
+ *
178
+ * @example
179
+ * ```tsx
180
+ * function Sales({ data }: { data: ChartDatum[] }) {
181
+ * const colors = useChartColors();
182
+ * return <BarChart data={data} colors={colors} />;
183
+ * }
184
+ * ```
185
+ */
186
+ export declare function useChartColors(explicit?: string[], element?: Element | null): string[];
187
+
139
188
  export { }
package/dist/charts.js CHANGED
@@ -1,7 +1,8 @@
1
- import { DEFAULT_CHART_COLORS as e } from "./charts/palette.js";
2
- import { AreaChart as t } from "./charts/AreaChart.js";
3
- import { BarChart as n } from "./charts/BarChart.js";
4
- import { LineChart as r } from "./charts/LineChart.js";
5
- import { PieChart as i } from "./charts/PieChart.js";
6
- import { RadarChart as a } from "./charts/RadarChart.js";
7
- export { t as AreaChart, n as BarChart, e as DEFAULT_CHART_COLORS, r as LineChart, i as PieChart, a as RadarChart };
1
+ import { CHART_COLOR_TOKEN_COUNT as e, DEFAULT_CHART_COLORS as t, resolveChartChrome as n, resolveChartColors as r } from "./charts/palette.js";
2
+ import { useChartColors as i } from "./charts/use-chart-colors.js";
3
+ import { AreaChart as a } from "./charts/AreaChart.js";
4
+ import { BarChart as o } from "./charts/BarChart.js";
5
+ import { LineChart as s } from "./charts/LineChart.js";
6
+ import { PieChart as c } from "./charts/PieChart.js";
7
+ import { RadarChart as l } from "./charts/RadarChart.js";
8
+ export { a as AreaChart, o as BarChart, e as CHART_COLOR_TOKEN_COUNT, t as DEFAULT_CHART_COLORS, s as LineChart, c as PieChart, l as RadarChart, n as resolveChartChrome, r as resolveChartColors, i as useChartColors };
@@ -0,0 +1,2 @@
1
+ const e=require("../../utils/cn.cjs"),t=require("../Avatar/Avatar.cjs"),n=require("./AvatarGroup.module.cjs");let r=require("react/jsx-runtime");function i({items:i,max:a=4,size:o=`md`,label:s,onOverflowClick:c,className:l}){let u=Math.max(0,a),d=i.slice(0,u),f=i.length-d.length;return(0,r.jsxs)(`div`,{role:`group`,"aria-label":s,className:e.cn(n.default.group,n.default[o],l),children:[d.map((e,i)=>(0,r.jsx)(`span`,{className:n.default.slot,children:(0,r.jsx)(t.Avatar,{src:e.src,name:e.name,alt:e.name,size:o})},`${e.name}-${i}`)),f>0?(0,r.jsx)(`span`,{className:n.default.slot,children:c?(0,r.jsxs)(`button`,{type:`button`,className:e.cn(n.default.overflow,n.default.overflowButton),"aria-label":`${f} more`,onClick:c,children:[`+`,f]}):(0,r.jsxs)(`span`,{className:n.default.overflow,"aria-label":`${f} more`,children:[`+`,f]})}):null]})}exports.AvatarGroup=i;
2
+ //# sourceMappingURL=AvatarGroup.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AvatarGroup.cjs","names":[],"sources":["../../../src/components/AvatarGroup/AvatarGroup.tsx"],"sourcesContent":["import { cn } from \"@/utils/cn\";\nimport { Avatar } from \"../Avatar\";\nimport type { AvatarSize } from \"../Avatar\";\nimport styles from \"./AvatarGroup.module.css\";\n\n/** One participant of the group. */\nexport interface AvatarGroupItem {\n /** Image URL. Falls back to initials from `name`. */\n src?: string;\n /** Full name — drives the initials and the accessible label. */\n name: string;\n}\n\nexport interface AvatarGroupProps {\n items: AvatarGroupItem[];\n /** How many avatars to show before collapsing into `+N`. Default `4`. */\n max?: number;\n size?: AvatarSize;\n /** Accessible name of the group, e.g. `\"Participantes\"`. */\n label?: string;\n /**\n * Called when the `+N` chip is activated — a natural hook for \"see all\n * participants\". Without it the chip is plain text and not focusable.\n */\n onOverflowClick?: () => void;\n className?: string;\n}\n\n/**\n * Overlapping row of avatars with a `+N` overflow chip — participants of a\n * meeting, assignees of a task, members of a team.\n *\n * The whole row is one `role=\"group\"` with a single accessible name, and each\n * avatar's name is exposed as its label. That is deliberate: announcing seven\n * separate images with no relation between them is noise, and the overflow chip\n * carries the remaining count so the total is never hidden from a screen reader.\n *\n * @example\n * ```tsx\n * <AvatarGroup\n * label=\"Participantes\"\n * max={3}\n * items={[\n * { name: \"Ada Lovelace\", src: ada },\n * { name: \"Grace Hopper\" },\n * { name: \"Alan Turing\" },\n * { name: \"Edsger Dijkstra\" },\n * ]}\n * onOverflowClick={() => setDrawerOpen(true)}\n * />\n * ```\n */\nexport function AvatarGroup({\n items,\n max = 4,\n size = \"md\",\n label,\n onOverflowClick,\n className,\n}: AvatarGroupProps) {\n const limit = Math.max(0, max);\n const visible = items.slice(0, limit);\n const overflow = items.length - visible.length;\n\n return (\n <div role=\"group\" aria-label={label} className={cn(styles.group, styles[size], className)}>\n {visible.map((item, index) => (\n <span key={`${item.name}-${index}`} className={styles.slot}>\n <Avatar src={item.src} name={item.name} alt={item.name} size={size} />\n </span>\n ))}\n\n {overflow > 0 ? (\n <span className={styles.slot}>\n {onOverflowClick ? (\n <button\n type=\"button\"\n className={cn(styles.overflow, styles.overflowButton)}\n aria-label={`${overflow} more`}\n onClick={onOverflowClick}\n >\n +{overflow}\n </button>\n ) : (\n <span className={styles.overflow} aria-label={`${overflow} more`}>\n +{overflow}\n </span>\n )}\n </span>\n ) : null}\n </div>\n );\n}\n"],"mappings":"iJAoDA,SAAgB,EAAY,CACxB,QACA,MAAM,EACN,OAAO,KACP,QACA,kBACA,aACiB,CACjB,IAAM,EAAQ,KAAK,IAAI,EAAG,CAAG,EACvB,EAAU,EAAM,MAAM,EAAG,CAAK,EAC9B,EAAW,EAAM,OAAS,EAAQ,OAExC,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,KAAK,QAAQ,aAAY,EAAO,UAAW,EAAA,GAAG,EAAA,QAAO,MAAO,EAAA,QAAO,GAAO,CAAS,WAAxF,CACK,EAAQ,KAAK,EAAM,KAChB,EAAA,EAAA,IAAA,CAAC,OAAD,CAAoC,UAAW,EAAA,QAAO,eAClD,EAAA,EAAA,IAAA,CAAC,EAAA,OAAD,CAAQ,IAAK,EAAK,IAAK,KAAM,EAAK,KAAM,IAAK,EAAK,KAAY,MAAO,CAAA,CACnE,EAFK,GAAG,EAAK,KAAK,GAAG,GAErB,CACT,EAEA,EAAW,GACR,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,cACnB,GACG,EAAA,EAAA,KAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,GAAG,EAAA,QAAO,SAAU,EAAA,QAAO,cAAc,EACpD,aAAY,GAAG,EAAS,OACxB,QAAS,WAJb,CAKC,IACK,CACE,KAER,EAAA,EAAA,KAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,SAAU,aAAY,GAAG,EAAS,gBAA1D,CAAkE,IAC5D,CACA,GAER,CAAA,EACN,IACH,GAEb"}
@@ -0,0 +1,39 @@
1
+ import { cn as e } from "../../utils/cn.js";
2
+ import { Avatar as t } from "../Avatar/Avatar.js";
3
+ import n from "./AvatarGroup.module.js";
4
+ import { jsx as r, jsxs as i } from "react/jsx-runtime";
5
+ //#region src/components/AvatarGroup/AvatarGroup.tsx
6
+ function a({ items: a, max: o = 4, size: s = "md", label: c, onOverflowClick: l, className: u }) {
7
+ let d = Math.max(0, o), f = a.slice(0, d), p = a.length - f.length;
8
+ return /* @__PURE__ */ i("div", {
9
+ role: "group",
10
+ "aria-label": c,
11
+ className: e(n.group, n[s], u),
12
+ children: [f.map((e, i) => /* @__PURE__ */ r("span", {
13
+ className: n.slot,
14
+ children: /* @__PURE__ */ r(t, {
15
+ src: e.src,
16
+ name: e.name,
17
+ alt: e.name,
18
+ size: s
19
+ })
20
+ }, `${e.name}-${i}`)), p > 0 ? /* @__PURE__ */ r("span", {
21
+ className: n.slot,
22
+ children: l ? /* @__PURE__ */ i("button", {
23
+ type: "button",
24
+ className: e(n.overflow, n.overflowButton),
25
+ "aria-label": `${p} more`,
26
+ onClick: l,
27
+ children: ["+", p]
28
+ }) : /* @__PURE__ */ i("span", {
29
+ className: n.overflow,
30
+ "aria-label": `${p} more`,
31
+ children: ["+", p]
32
+ })
33
+ }) : null]
34
+ });
35
+ }
36
+ //#endregion
37
+ export { a as AvatarGroup };
38
+
39
+ //# sourceMappingURL=AvatarGroup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AvatarGroup.js","names":[],"sources":["../../../src/components/AvatarGroup/AvatarGroup.tsx"],"sourcesContent":["import { cn } from \"@/utils/cn\";\nimport { Avatar } from \"../Avatar\";\nimport type { AvatarSize } from \"../Avatar\";\nimport styles from \"./AvatarGroup.module.css\";\n\n/** One participant of the group. */\nexport interface AvatarGroupItem {\n /** Image URL. Falls back to initials from `name`. */\n src?: string;\n /** Full name — drives the initials and the accessible label. */\n name: string;\n}\n\nexport interface AvatarGroupProps {\n items: AvatarGroupItem[];\n /** How many avatars to show before collapsing into `+N`. Default `4`. */\n max?: number;\n size?: AvatarSize;\n /** Accessible name of the group, e.g. `\"Participantes\"`. */\n label?: string;\n /**\n * Called when the `+N` chip is activated — a natural hook for \"see all\n * participants\". Without it the chip is plain text and not focusable.\n */\n onOverflowClick?: () => void;\n className?: string;\n}\n\n/**\n * Overlapping row of avatars with a `+N` overflow chip — participants of a\n * meeting, assignees of a task, members of a team.\n *\n * The whole row is one `role=\"group\"` with a single accessible name, and each\n * avatar's name is exposed as its label. That is deliberate: announcing seven\n * separate images with no relation between them is noise, and the overflow chip\n * carries the remaining count so the total is never hidden from a screen reader.\n *\n * @example\n * ```tsx\n * <AvatarGroup\n * label=\"Participantes\"\n * max={3}\n * items={[\n * { name: \"Ada Lovelace\", src: ada },\n * { name: \"Grace Hopper\" },\n * { name: \"Alan Turing\" },\n * { name: \"Edsger Dijkstra\" },\n * ]}\n * onOverflowClick={() => setDrawerOpen(true)}\n * />\n * ```\n */\nexport function AvatarGroup({\n items,\n max = 4,\n size = \"md\",\n label,\n onOverflowClick,\n className,\n}: AvatarGroupProps) {\n const limit = Math.max(0, max);\n const visible = items.slice(0, limit);\n const overflow = items.length - visible.length;\n\n return (\n <div role=\"group\" aria-label={label} className={cn(styles.group, styles[size], className)}>\n {visible.map((item, index) => (\n <span key={`${item.name}-${index}`} className={styles.slot}>\n <Avatar src={item.src} name={item.name} alt={item.name} size={size} />\n </span>\n ))}\n\n {overflow > 0 ? (\n <span className={styles.slot}>\n {onOverflowClick ? (\n <button\n type=\"button\"\n className={cn(styles.overflow, styles.overflowButton)}\n aria-label={`${overflow} more`}\n onClick={onOverflowClick}\n >\n +{overflow}\n </button>\n ) : (\n <span className={styles.overflow} aria-label={`${overflow} more`}>\n +{overflow}\n </span>\n )}\n </span>\n ) : null}\n </div>\n );\n}\n"],"mappings":";;;;;AAoDA,SAAgB,EAAY,EACxB,UACA,SAAM,GACN,UAAO,MACP,UACA,oBACA,gBACiB;CACjB,IAAM,IAAQ,KAAK,IAAI,GAAG,CAAG,GACvB,IAAU,EAAM,MAAM,GAAG,CAAK,GAC9B,IAAW,EAAM,SAAS,EAAQ;CAExC,OACI,kBAAC,OAAD;EAAK,MAAK;EAAQ,cAAY;EAAO,WAAW,EAAG,EAAO,OAAO,EAAO,IAAO,CAAS;YAAxF,CACK,EAAQ,KAAK,GAAM,MAChB,kBAAC,QAAD;GAAoC,WAAW,EAAO;aAClD,kBAAC,GAAD;IAAQ,KAAK,EAAK;IAAK,MAAM,EAAK;IAAM,KAAK,EAAK;IAAY;GAAO,CAAA;EACnE,GAFK,GAAG,EAAK,KAAK,GAAG,GAErB,CACT,GAEA,IAAW,IACR,kBAAC,QAAD;GAAM,WAAW,EAAO;aACnB,IACG,kBAAC,UAAD;IACI,MAAK;IACL,WAAW,EAAG,EAAO,UAAU,EAAO,cAAc;IACpD,cAAY,GAAG,EAAS;IACxB,SAAS;cAJb,CAKC,KACK,CACE;QAER,kBAAC,QAAD;IAAM,WAAW,EAAO;IAAU,cAAY,GAAG,EAAS;cAA1D,CAAkE,KAC5D,CACA;;EAER,CAAA,IACN,IACH;;AAEb"}
@@ -0,0 +1,2 @@
1
+ var e=`tempest_group_qCSYk`,t=`tempest_slot_KyJQJ`,n=`tempest_xs_PPTP5`,r=`tempest_sm_ptoRA`,i=`tempest_md_YbE61`,a=`tempest_lg_oAEMY`,o=`tempest_xl_78RZG`,s=`tempest_overflow_JZIo-`,c=`tempest_overflowButton_hSf-2`,l={group:e,slot:t,xs:n,sm:r,md:i,lg:a,xl:o,overflow:s,overflowButton:c};exports.default=l,exports.group=e,exports.lg=a,exports.md=i,exports.overflow=s,exports.overflowButton=c,exports.slot=t,exports.sm=r,exports.xl=o,exports.xs=n;
2
+ //# sourceMappingURL=AvatarGroup.module.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AvatarGroup.module.cjs","names":[],"sources":["../../../src/components/AvatarGroup/AvatarGroup.module.css"],"sourcesContent":[".group {\n display: inline-flex;\n align-items: center;\n}\n\n/**\n * Each slot pulls the next avatar over the previous one. The overlap is a\n * fraction of the avatar size so every size keeps the same proportion.\n */\n.slot {\n display: inline-flex;\n border-radius: var(--tempest-radius-full);\n box-shadow: 0 0 0 2px var(--tempest-bg);\n}\n\n.slot + .slot {\n margin-inline-start: var(--tempest-avatar-overlap, -10px);\n}\n\n.xs .slot + .slot {\n margin-inline-start: var(--tempest-avatar-overlap, -8px);\n}\n\n.sm .slot + .slot {\n margin-inline-start: var(--tempest-avatar-overlap, -10px);\n}\n\n.md .slot + .slot {\n margin-inline-start: var(--tempest-avatar-overlap, -12px);\n}\n\n.lg .slot + .slot {\n margin-inline-start: var(--tempest-avatar-overlap, -16px);\n}\n\n.xl .slot + .slot {\n margin-inline-start: var(--tempest-avatar-overlap, -22px);\n}\n\n.overflow {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n width: 40px;\n height: 40px;\n border: 0;\n border-radius: var(--tempest-radius-full);\n background-color: var(--tempest-surface-3);\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n font-weight: var(--tempest-weight-medium);\n font-variant-numeric: tabular-nums;\n}\n\n.xs .overflow {\n width: 24px;\n height: 24px;\n font-size: var(--tempest-text-2xs);\n}\n\n.sm .overflow {\n width: 32px;\n height: 32px;\n}\n\n.lg .overflow {\n width: 56px;\n height: 56px;\n font-size: var(--tempest-text-sm);\n}\n\n.xl .overflow {\n width: 80px;\n height: 80px;\n font-size: var(--tempest-text-base);\n}\n\n.overflowButton {\n cursor: pointer;\n transition: background-color var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.overflowButton:hover {\n background-color: var(--tempest-border-strong);\n color: var(--tempest-text);\n}\n\n.overflowButton:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .overflowButton {\n transition: none;\n }\n}\n"],"mappings":""}
@@ -0,0 +1,16 @@
1
+ //#region src/components/AvatarGroup/AvatarGroup.module.css
2
+ var e = "tempest_group_qCSYk", t = "tempest_slot_KyJQJ", n = "tempest_xs_PPTP5", r = "tempest_sm_ptoRA", i = "tempest_md_YbE61", a = "tempest_lg_oAEMY", o = "tempest_xl_78RZG", s = "tempest_overflow_JZIo-", c = "tempest_overflowButton_hSf-2", l = {
3
+ group: e,
4
+ slot: t,
5
+ xs: n,
6
+ sm: r,
7
+ md: i,
8
+ lg: a,
9
+ xl: o,
10
+ overflow: s,
11
+ overflowButton: c
12
+ };
13
+ //#endregion
14
+ export { l as default, e as group, a as lg, i as md, s as overflow, c as overflowButton, t as slot, r as sm, o as xl, n as xs };
15
+
16
+ //# sourceMappingURL=AvatarGroup.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AvatarGroup.module.js","names":[],"sources":["../../../src/components/AvatarGroup/AvatarGroup.module.css"],"sourcesContent":[".group {\n display: inline-flex;\n align-items: center;\n}\n\n/**\n * Each slot pulls the next avatar over the previous one. The overlap is a\n * fraction of the avatar size so every size keeps the same proportion.\n */\n.slot {\n display: inline-flex;\n border-radius: var(--tempest-radius-full);\n box-shadow: 0 0 0 2px var(--tempest-bg);\n}\n\n.slot + .slot {\n margin-inline-start: var(--tempest-avatar-overlap, -10px);\n}\n\n.xs .slot + .slot {\n margin-inline-start: var(--tempest-avatar-overlap, -8px);\n}\n\n.sm .slot + .slot {\n margin-inline-start: var(--tempest-avatar-overlap, -10px);\n}\n\n.md .slot + .slot {\n margin-inline-start: var(--tempest-avatar-overlap, -12px);\n}\n\n.lg .slot + .slot {\n margin-inline-start: var(--tempest-avatar-overlap, -16px);\n}\n\n.xl .slot + .slot {\n margin-inline-start: var(--tempest-avatar-overlap, -22px);\n}\n\n.overflow {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n width: 40px;\n height: 40px;\n border: 0;\n border-radius: var(--tempest-radius-full);\n background-color: var(--tempest-surface-3);\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n font-weight: var(--tempest-weight-medium);\n font-variant-numeric: tabular-nums;\n}\n\n.xs .overflow {\n width: 24px;\n height: 24px;\n font-size: var(--tempest-text-2xs);\n}\n\n.sm .overflow {\n width: 32px;\n height: 32px;\n}\n\n.lg .overflow {\n width: 56px;\n height: 56px;\n font-size: var(--tempest-text-sm);\n}\n\n.xl .overflow {\n width: 80px;\n height: 80px;\n font-size: var(--tempest-text-base);\n}\n\n.overflowButton {\n cursor: pointer;\n transition: background-color var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.overflowButton:hover {\n background-color: var(--tempest-border-strong);\n color: var(--tempest-text);\n}\n\n.overflowButton:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .overflowButton {\n transition: none;\n }\n}\n"],"mappings":""}
@@ -0,0 +1,2 @@
1
+ const e=require("../../utils/cn.cjs"),t=require("../../hooks/use-scroll-lock.cjs"),n=require("../../hooks/use-focus-trap.cjs"),r=require("../Portal/Portal.cjs"),i=require("./Lightbox.module.cjs");let a=require("react"),o=require("react/jsx-runtime"),s=require("lucide-react");function c({items:c,open:l,index:u=0,onIndexChange:d,onClose:f,showThumbnails:p,showCounter:m=!0,loop:h=!0,closeLabel:g=`Close`,previousLabel:_=`Previous image`,nextLabel:v=`Next image`,className:y}){let b=(0,a.useRef)(null),[x,S]=(0,a.useState)(u),C=d!==void 0,w=C?u:x;n.useFocusTrap(b,l),t.useScrollLock(l);let T=(0,a.useCallback)(e=>{if(c.length===0)return;let t=h?(e+c.length)%c.length:Math.max(0,Math.min(e,c.length-1));C||S(t),d?.(t)},[C,c.length,h,d]);if((0,a.useEffect)(()=>{C||S(u)},[u,C]),(0,a.useEffect)(()=>{if(!l)return;function e(e){switch(e.key){case`Escape`:f();break;case`ArrowRight`:T(w+1);break;case`ArrowLeft`:T(w-1);break;case`Home`:T(0);break;case`End`:T(c.length-1);break;default:return}e.preventDefault()}return document.addEventListener(`keydown`,e),()=>document.removeEventListener(`keydown`,e)},[w,c.length,T,f,l]),(0,a.useEffect)(()=>{if(!(!l||typeof Image>`u`))for(let e of[-1,1]){let t=c[(w+e+c.length)%c.length];if(!t)continue;let n=new Image;n.src=t.src}},[w,c,l]),!l||c.length===0)return null;let E=c[Math.max(0,Math.min(w,c.length-1))],D=p??c.length>1,O=c.length>1;return(0,o.jsx)(r.Portal,{children:(0,o.jsxs)(`div`,{ref:b,role:`dialog`,"aria-modal":`true`,"aria-label":E.alt,className:e.cn(i.default.overlay,y),children:[(0,o.jsxs)(`div`,{className:i.default.topBar,children:[m&&O?(0,o.jsxs)(`span`,{className:i.default.counter,children:[w+1,` / `,c.length]}):(0,o.jsx)(`span`,{}),(0,o.jsx)(`button`,{type:`button`,className:i.default.iconButton,"aria-label":g,onClick:f,children:(0,o.jsx)(s.X,{size:20})})]}),(0,o.jsxs)(`div`,{className:i.default.stage,children:[O?(0,o.jsx)(`button`,{type:`button`,className:e.cn(i.default.iconButton,i.default.nav),"aria-label":_,disabled:!h&&w===0,onClick:()=>T(w-1),children:(0,o.jsx)(s.ChevronLeft,{size:24})}):null,(0,o.jsxs)(`figure`,{className:i.default.figure,children:[(0,o.jsx)(`img`,{src:E.src,alt:E.alt,className:i.default.image}),E.caption?(0,o.jsx)(`figcaption`,{className:i.default.caption,children:E.caption}):null]}),O?(0,o.jsx)(`button`,{type:`button`,className:e.cn(i.default.iconButton,i.default.nav),"aria-label":v,disabled:!h&&w===c.length-1,onClick:()=>T(w+1),children:(0,o.jsx)(s.ChevronRight,{size:24})}):null]}),D&&O?(0,o.jsx)(`div`,{className:i.default.thumbnails,role:`tablist`,"aria-label":E.alt,children:c.map((t,n)=>(0,o.jsx)(`button`,{type:`button`,role:`tab`,"aria-selected":n===w,"aria-label":t.alt,className:e.cn(i.default.thumbnail,n===w&&i.default.thumbnailActive),onClick:()=>T(n),children:(0,o.jsx)(`img`,{src:t.thumbnail??t.src,alt:``,className:i.default.thumbnailImage})},`${t.src}-${n}`))}):null]})})}exports.Lightbox=c;
2
+ //# sourceMappingURL=Lightbox.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Lightbox.cjs","names":[],"sources":["../../../src/components/Lightbox/Lightbox.tsx"],"sourcesContent":["import { ChevronLeft, ChevronRight, X } from \"lucide-react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useFocusTrap } from \"@/hooks/use-focus-trap\";\nimport { useScrollLock } from \"@/hooks/use-scroll-lock\";\nimport { cn } from \"@/utils/cn\";\nimport { Portal } from \"../Portal\";\nimport styles from \"./Lightbox.module.css\";\n\n/** One item of the gallery. */\nexport interface LightboxItem {\n /** Full-size image URL. */\n src: string;\n /** Alt text. Required: a gallery of unlabeled images is unusable with a screen reader. */\n alt: string;\n /** Caption rendered under the image. */\n caption?: string;\n /** Thumbnail URL for the strip. Falls back to `src`. */\n thumbnail?: string;\n}\n\nexport interface LightboxProps {\n items: LightboxItem[];\n /** Whether the overlay is open. */\n open: boolean;\n /** Index shown while open. Default `0`. */\n index?: number;\n onIndexChange?: (index: number) => void;\n onClose: () => void;\n /** Renders the thumbnail strip. Default `true` when there is more than one item. */\n showThumbnails?: boolean;\n /** Renders the `3 / 12` counter. Default `true`. */\n showCounter?: boolean;\n /**\n * Wrap around at the ends. Default `true` — in a photo viewer, hitting a dead\n * end at the last image reads as a bug more often than as a boundary.\n */\n loop?: boolean;\n closeLabel?: string;\n previousLabel?: string;\n nextLabel?: string;\n className?: string;\n}\n\n/**\n * Full-screen image viewer with keyboard navigation.\n *\n * `Esc` closes, `←`/`→` walk the gallery, `Home`/`End` jump to the ends. Focus is\n * trapped inside while open and the page behind is scroll-locked, so the overlay\n * behaves like the dialog it is instead of a fancy `<div>` the keyboard can escape.\n *\n * Only the current image is mounted; neighbours are **preloaded** via `Image()` so\n * pressing `→` does not flash an empty frame.\n *\n * @example\n * ```tsx\n * const [open, setOpen] = useState(false);\n * const [index, setIndex] = useState(0);\n *\n * <Lightbox\n * open={open}\n * items={photos.map((p) => ({ src: p.url, alt: p.description }))}\n * index={index}\n * onIndexChange={setIndex}\n * onClose={() => setOpen(false)}\n * />\n * ```\n */\nexport function Lightbox({\n items,\n open,\n index = 0,\n onIndexChange,\n onClose,\n showThumbnails,\n showCounter = true,\n loop = true,\n closeLabel = \"Close\",\n previousLabel = \"Previous image\",\n nextLabel = \"Next image\",\n className,\n}: LightboxProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [internalIndex, setInternalIndex] = useState(index);\n const isControlled = onIndexChange !== undefined;\n const current = isControlled ? index : internalIndex;\n\n useFocusTrap(containerRef, open);\n useScrollLock(open);\n\n const move = useCallback(\n (next: number): void => {\n if (items.length === 0) return;\n const target = loop\n ? (next + items.length) % items.length\n : Math.max(0, Math.min(next, items.length - 1));\n if (!isControlled) setInternalIndex(target);\n onIndexChange?.(target);\n },\n [isControlled, items.length, loop, onIndexChange],\n );\n\n useEffect(() => {\n if (!isControlled) setInternalIndex(index);\n }, [index, isControlled]);\n\n useEffect(() => {\n if (!open) return;\n function onKeyDown(event: KeyboardEvent): void {\n switch (event.key) {\n case \"Escape\":\n onClose();\n break;\n case \"ArrowRight\":\n move(current + 1);\n break;\n case \"ArrowLeft\":\n move(current - 1);\n break;\n case \"Home\":\n move(0);\n break;\n case \"End\":\n move(items.length - 1);\n break;\n default:\n return;\n }\n event.preventDefault();\n }\n document.addEventListener(\"keydown\", onKeyDown);\n return () => document.removeEventListener(\"keydown\", onKeyDown);\n }, [current, items.length, move, onClose, open]);\n\n useEffect(() => {\n if (!open || typeof Image === \"undefined\") return;\n for (const offset of [-1, 1]) {\n const neighbour = items[(current + offset + items.length) % items.length];\n if (!neighbour) continue;\n const preload = new Image();\n preload.src = neighbour.src;\n }\n }, [current, items, open]);\n\n if (!open || items.length === 0) return null;\n\n const item = items[Math.max(0, Math.min(current, items.length - 1))];\n const withThumbnails = showThumbnails ?? items.length > 1;\n const multiple = items.length > 1;\n\n return (\n <Portal>\n <div\n ref={containerRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={item.alt}\n className={cn(styles.overlay, className)}\n >\n <div className={styles.topBar}>\n {showCounter && multiple ? (\n <span className={styles.counter}>\n {current + 1} / {items.length}\n </span>\n ) : (\n <span />\n )}\n <button\n type=\"button\"\n className={styles.iconButton}\n aria-label={closeLabel}\n onClick={onClose}\n >\n <X size={20} />\n </button>\n </div>\n\n <div className={styles.stage}>\n {multiple ? (\n <button\n type=\"button\"\n className={cn(styles.iconButton, styles.nav)}\n aria-label={previousLabel}\n disabled={!loop && current === 0}\n onClick={() => move(current - 1)}\n >\n <ChevronLeft size={24} />\n </button>\n ) : null}\n\n <figure className={styles.figure}>\n <img src={item.src} alt={item.alt} className={styles.image} />\n {item.caption ? (\n <figcaption className={styles.caption}>{item.caption}</figcaption>\n ) : null}\n </figure>\n\n {multiple ? (\n <button\n type=\"button\"\n className={cn(styles.iconButton, styles.nav)}\n aria-label={nextLabel}\n disabled={!loop && current === items.length - 1}\n onClick={() => move(current + 1)}\n >\n <ChevronRight size={24} />\n </button>\n ) : null}\n </div>\n\n {withThumbnails && multiple ? (\n <div className={styles.thumbnails} role=\"tablist\" aria-label={item.alt}>\n {items.map((entry, entryIndex) => (\n <button\n key={`${entry.src}-${entryIndex}`}\n type=\"button\"\n role=\"tab\"\n aria-selected={entryIndex === current}\n aria-label={entry.alt}\n className={cn(\n styles.thumbnail,\n entryIndex === current && styles.thumbnailActive,\n )}\n onClick={() => move(entryIndex)}\n >\n <img\n src={entry.thumbnail ?? entry.src}\n alt=\"\"\n className={styles.thumbnailImage}\n />\n </button>\n ))}\n </div>\n ) : null}\n </div>\n </Portal>\n );\n}\n"],"mappings":"oRAmEA,SAAgB,EAAS,CACrB,QACA,OACA,QAAQ,EACR,gBACA,UACA,iBACA,cAAc,GACd,OAAO,GACP,aAAa,QACb,gBAAgB,iBAChB,YAAY,aACZ,aACc,CACd,IAAM,GAAA,EAAA,EAAA,OAAA,CAAsC,IAAI,EAC1C,CAAC,EAAe,IAAA,EAAA,EAAA,SAAA,CAA6B,CAAK,EAClD,EAAe,IAAkB,IAAA,GACjC,EAAU,EAAe,EAAQ,EAEvC,EAAA,aAAa,EAAc,CAAI,EAC/B,EAAA,cAAc,CAAI,EAElB,IAAM,GAAA,EAAA,EAAA,YAAA,CACD,GAAuB,CACpB,GAAI,EAAM,SAAW,EAAG,OACxB,IAAM,EAAS,GACR,EAAO,EAAM,QAAU,EAAM,OAC9B,KAAK,IAAI,EAAG,KAAK,IAAI,EAAM,EAAM,OAAS,CAAC,CAAC,EAC7C,GAAc,EAAiB,CAAM,EAC1C,IAAgB,CAAM,CAC1B,EACA,CAAC,EAAc,EAAM,OAAQ,EAAM,CAAa,CACpD,EA4CA,IA1CA,EAAA,EAAA,UAAA,KAAgB,CACP,GAAc,EAAiB,CAAK,CAC7C,EAAG,CAAC,EAAO,CAAY,CAAC,GAExB,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,EAAM,OACX,SAAS,EAAU,EAA4B,CAC3C,OAAQ,EAAM,IAAd,CACI,IAAK,SACD,EAAQ,EACR,MACJ,IAAK,aACD,EAAK,EAAU,CAAC,EAChB,MACJ,IAAK,YACD,EAAK,EAAU,CAAC,EAChB,MACJ,IAAK,OACD,EAAK,CAAC,EACN,MACJ,IAAK,MACD,EAAK,EAAM,OAAS,CAAC,EACrB,MACJ,QACI,MACR,CACA,EAAM,eAAe,CACzB,CAEA,OADA,SAAS,iBAAiB,UAAW,CAAS,MACjC,SAAS,oBAAoB,UAAW,CAAS,CAClE,EAAG,CAAC,EAAS,EAAM,OAAQ,EAAM,EAAS,CAAI,CAAC,GAE/C,EAAA,EAAA,UAAA,KAAgB,CACR,MAAC,GAAQ,OAAO,MAAU,KAC9B,IAAK,IAAM,IAAU,CAAC,GAAI,CAAC,EAAG,CAC1B,IAAM,EAAY,GAAO,EAAU,EAAS,EAAM,QAAU,EAAM,QAClE,GAAI,CAAC,EAAW,SAChB,IAAM,EAAU,IAAI,MACpB,EAAQ,IAAM,EAAU,GAC5B,CACJ,EAAG,CAAC,EAAS,EAAO,CAAI,CAAC,EAErB,CAAC,GAAQ,EAAM,SAAW,EAAG,OAAO,KAExC,IAAM,EAAO,EAAM,KAAK,IAAI,EAAG,KAAK,IAAI,EAAS,EAAM,OAAS,CAAC,CAAC,GAC5D,EAAiB,GAAkB,EAAM,OAAS,EAClD,EAAW,EAAM,OAAS,EAEhC,OACI,EAAA,EAAA,IAAA,CAAC,EAAA,OAAD,CAAA,UACI,EAAA,EAAA,KAAA,CAAC,MAAD,CACI,IAAK,EACL,KAAK,SACL,aAAW,OACX,aAAY,EAAK,IACjB,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,CAAS,WAL3C,EAOI,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,gBAAvB,CACK,GAAe,GACZ,EAAA,EAAA,KAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,iBAAxB,CACK,EAAU,EAAE,MAAI,EAAM,MACrB,KAEN,EAAA,EAAA,IAAA,CAAC,OAAD,CAAO,CAAA,GAEX,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,WAClB,aAAY,EACZ,QAAS,YAET,EAAA,EAAA,IAAA,CAAC,EAAA,EAAD,CAAG,KAAM,EAAK,CAAA,CACV,CAAA,CACP,KAEL,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,eAAvB,CACK,GACG,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,GAAG,EAAA,QAAO,WAAY,EAAA,QAAO,GAAG,EAC3C,aAAY,EACZ,SAAU,CAAC,GAAQ,IAAY,EAC/B,YAAe,EAAK,EAAU,CAAC,YAE/B,EAAA,EAAA,IAAA,CAAC,EAAA,YAAD,CAAa,KAAM,EAAK,CAAA,CACpB,CAAA,EACR,MAEJ,EAAA,EAAA,KAAA,CAAC,SAAD,CAAQ,UAAW,EAAA,QAAO,gBAA1B,EACI,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,IAAK,EAAK,IAAK,IAAK,EAAK,IAAK,UAAW,EAAA,QAAO,KAAQ,CAAA,EAC5D,EAAK,SACF,EAAA,EAAA,IAAA,CAAC,aAAD,CAAY,UAAW,EAAA,QAAO,iBAAU,EAAK,OAAoB,CAAA,EACjE,IACA,IAEP,GACG,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,GAAG,EAAA,QAAO,WAAY,EAAA,QAAO,GAAG,EAC3C,aAAY,EACZ,SAAU,CAAC,GAAQ,IAAY,EAAM,OAAS,EAC9C,YAAe,EAAK,EAAU,CAAC,YAE/B,EAAA,EAAA,IAAA,CAAC,EAAA,aAAD,CAAc,KAAM,EAAK,CAAA,CACrB,CAAA,EACR,IACH,IAEJ,GAAkB,GACf,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,WAAY,KAAK,UAAU,aAAY,EAAK,aAC9D,EAAM,KAAK,EAAO,KACf,EAAA,EAAA,IAAA,CAAC,SAAD,CAEI,KAAK,SACL,KAAK,MACL,gBAAe,IAAe,EAC9B,aAAY,EAAM,IAClB,UAAW,EAAA,GACP,EAAA,QAAO,UACP,IAAe,GAAW,EAAA,QAAO,eACrC,EACA,YAAe,EAAK,CAAU,YAE9B,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,IAAK,EAAM,WAAa,EAAM,IAC9B,IAAI,GACJ,UAAW,EAAA,QAAO,cACrB,CAAA,CACG,EAhBC,GAAG,EAAM,IAAI,GAAG,GAgBjB,CACX,CACA,CAAA,EACL,IACH,GACD,CAAA,CAEhB"}
@@ -0,0 +1,148 @@
1
+ import { cn as e } from "../../utils/cn.js";
2
+ import { useScrollLock as t } from "../../hooks/use-scroll-lock.js";
3
+ import { useFocusTrap as n } from "../../hooks/use-focus-trap.js";
4
+ import { Portal as r } from "../Portal/Portal.js";
5
+ import i from "./Lightbox.module.js";
6
+ import { useCallback as a, useEffect as o, useRef as s, useState as c } from "react";
7
+ import { jsx as l, jsxs as u } from "react/jsx-runtime";
8
+ import { ChevronLeft as d, ChevronRight as f, X as p } from "lucide-react";
9
+ //#region src/components/Lightbox/Lightbox.tsx
10
+ function m({ items: m, open: h, index: g = 0, onIndexChange: _, onClose: v, showThumbnails: y, showCounter: b = !0, loop: x = !0, closeLabel: S = "Close", previousLabel: C = "Previous image", nextLabel: w = "Next image", className: T }) {
11
+ let E = s(null), [D, O] = c(g), k = _ !== void 0, A = k ? g : D;
12
+ n(E, h), t(h);
13
+ let j = a((e) => {
14
+ if (m.length === 0) return;
15
+ let t = x ? (e + m.length) % m.length : Math.max(0, Math.min(e, m.length - 1));
16
+ k || O(t), _?.(t);
17
+ }, [
18
+ k,
19
+ m.length,
20
+ x,
21
+ _
22
+ ]);
23
+ if (o(() => {
24
+ k || O(g);
25
+ }, [g, k]), o(() => {
26
+ if (!h) return;
27
+ function e(e) {
28
+ switch (e.key) {
29
+ case "Escape":
30
+ v();
31
+ break;
32
+ case "ArrowRight":
33
+ j(A + 1);
34
+ break;
35
+ case "ArrowLeft":
36
+ j(A - 1);
37
+ break;
38
+ case "Home":
39
+ j(0);
40
+ break;
41
+ case "End":
42
+ j(m.length - 1);
43
+ break;
44
+ default: return;
45
+ }
46
+ e.preventDefault();
47
+ }
48
+ return document.addEventListener("keydown", e), () => document.removeEventListener("keydown", e);
49
+ }, [
50
+ A,
51
+ m.length,
52
+ j,
53
+ v,
54
+ h
55
+ ]), o(() => {
56
+ if (!(!h || typeof Image > "u")) for (let e of [-1, 1]) {
57
+ let t = m[(A + e + m.length) % m.length];
58
+ if (!t) continue;
59
+ let n = new Image();
60
+ n.src = t.src;
61
+ }
62
+ }, [
63
+ A,
64
+ m,
65
+ h
66
+ ]), !h || m.length === 0) return null;
67
+ let M = m[Math.max(0, Math.min(A, m.length - 1))], N = y ?? m.length > 1, P = m.length > 1;
68
+ return /* @__PURE__ */ l(r, { children: /* @__PURE__ */ u("div", {
69
+ ref: E,
70
+ role: "dialog",
71
+ "aria-modal": "true",
72
+ "aria-label": M.alt,
73
+ className: e(i.overlay, T),
74
+ children: [
75
+ /* @__PURE__ */ u("div", {
76
+ className: i.topBar,
77
+ children: [b && P ? /* @__PURE__ */ u("span", {
78
+ className: i.counter,
79
+ children: [
80
+ A + 1,
81
+ " / ",
82
+ m.length
83
+ ]
84
+ }) : /* @__PURE__ */ l("span", {}), /* @__PURE__ */ l("button", {
85
+ type: "button",
86
+ className: i.iconButton,
87
+ "aria-label": S,
88
+ onClick: v,
89
+ children: /* @__PURE__ */ l(p, { size: 20 })
90
+ })]
91
+ }),
92
+ /* @__PURE__ */ u("div", {
93
+ className: i.stage,
94
+ children: [
95
+ P ? /* @__PURE__ */ l("button", {
96
+ type: "button",
97
+ className: e(i.iconButton, i.nav),
98
+ "aria-label": C,
99
+ disabled: !x && A === 0,
100
+ onClick: () => j(A - 1),
101
+ children: /* @__PURE__ */ l(d, { size: 24 })
102
+ }) : null,
103
+ /* @__PURE__ */ u("figure", {
104
+ className: i.figure,
105
+ children: [/* @__PURE__ */ l("img", {
106
+ src: M.src,
107
+ alt: M.alt,
108
+ className: i.image
109
+ }), M.caption ? /* @__PURE__ */ l("figcaption", {
110
+ className: i.caption,
111
+ children: M.caption
112
+ }) : null]
113
+ }),
114
+ P ? /* @__PURE__ */ l("button", {
115
+ type: "button",
116
+ className: e(i.iconButton, i.nav),
117
+ "aria-label": w,
118
+ disabled: !x && A === m.length - 1,
119
+ onClick: () => j(A + 1),
120
+ children: /* @__PURE__ */ l(f, { size: 24 })
121
+ }) : null
122
+ ]
123
+ }),
124
+ N && P ? /* @__PURE__ */ l("div", {
125
+ className: i.thumbnails,
126
+ role: "tablist",
127
+ "aria-label": M.alt,
128
+ children: m.map((t, n) => /* @__PURE__ */ l("button", {
129
+ type: "button",
130
+ role: "tab",
131
+ "aria-selected": n === A,
132
+ "aria-label": t.alt,
133
+ className: e(i.thumbnail, n === A && i.thumbnailActive),
134
+ onClick: () => j(n),
135
+ children: /* @__PURE__ */ l("img", {
136
+ src: t.thumbnail ?? t.src,
137
+ alt: "",
138
+ className: i.thumbnailImage
139
+ })
140
+ }, `${t.src}-${n}`))
141
+ }) : null
142
+ ]
143
+ }) });
144
+ }
145
+ //#endregion
146
+ export { m as Lightbox };
147
+
148
+ //# sourceMappingURL=Lightbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Lightbox.js","names":[],"sources":["../../../src/components/Lightbox/Lightbox.tsx"],"sourcesContent":["import { ChevronLeft, ChevronRight, X } from \"lucide-react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useFocusTrap } from \"@/hooks/use-focus-trap\";\nimport { useScrollLock } from \"@/hooks/use-scroll-lock\";\nimport { cn } from \"@/utils/cn\";\nimport { Portal } from \"../Portal\";\nimport styles from \"./Lightbox.module.css\";\n\n/** One item of the gallery. */\nexport interface LightboxItem {\n /** Full-size image URL. */\n src: string;\n /** Alt text. Required: a gallery of unlabeled images is unusable with a screen reader. */\n alt: string;\n /** Caption rendered under the image. */\n caption?: string;\n /** Thumbnail URL for the strip. Falls back to `src`. */\n thumbnail?: string;\n}\n\nexport interface LightboxProps {\n items: LightboxItem[];\n /** Whether the overlay is open. */\n open: boolean;\n /** Index shown while open. Default `0`. */\n index?: number;\n onIndexChange?: (index: number) => void;\n onClose: () => void;\n /** Renders the thumbnail strip. Default `true` when there is more than one item. */\n showThumbnails?: boolean;\n /** Renders the `3 / 12` counter. Default `true`. */\n showCounter?: boolean;\n /**\n * Wrap around at the ends. Default `true` — in a photo viewer, hitting a dead\n * end at the last image reads as a bug more often than as a boundary.\n */\n loop?: boolean;\n closeLabel?: string;\n previousLabel?: string;\n nextLabel?: string;\n className?: string;\n}\n\n/**\n * Full-screen image viewer with keyboard navigation.\n *\n * `Esc` closes, `←`/`→` walk the gallery, `Home`/`End` jump to the ends. Focus is\n * trapped inside while open and the page behind is scroll-locked, so the overlay\n * behaves like the dialog it is instead of a fancy `<div>` the keyboard can escape.\n *\n * Only the current image is mounted; neighbours are **preloaded** via `Image()` so\n * pressing `→` does not flash an empty frame.\n *\n * @example\n * ```tsx\n * const [open, setOpen] = useState(false);\n * const [index, setIndex] = useState(0);\n *\n * <Lightbox\n * open={open}\n * items={photos.map((p) => ({ src: p.url, alt: p.description }))}\n * index={index}\n * onIndexChange={setIndex}\n * onClose={() => setOpen(false)}\n * />\n * ```\n */\nexport function Lightbox({\n items,\n open,\n index = 0,\n onIndexChange,\n onClose,\n showThumbnails,\n showCounter = true,\n loop = true,\n closeLabel = \"Close\",\n previousLabel = \"Previous image\",\n nextLabel = \"Next image\",\n className,\n}: LightboxProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [internalIndex, setInternalIndex] = useState(index);\n const isControlled = onIndexChange !== undefined;\n const current = isControlled ? index : internalIndex;\n\n useFocusTrap(containerRef, open);\n useScrollLock(open);\n\n const move = useCallback(\n (next: number): void => {\n if (items.length === 0) return;\n const target = loop\n ? (next + items.length) % items.length\n : Math.max(0, Math.min(next, items.length - 1));\n if (!isControlled) setInternalIndex(target);\n onIndexChange?.(target);\n },\n [isControlled, items.length, loop, onIndexChange],\n );\n\n useEffect(() => {\n if (!isControlled) setInternalIndex(index);\n }, [index, isControlled]);\n\n useEffect(() => {\n if (!open) return;\n function onKeyDown(event: KeyboardEvent): void {\n switch (event.key) {\n case \"Escape\":\n onClose();\n break;\n case \"ArrowRight\":\n move(current + 1);\n break;\n case \"ArrowLeft\":\n move(current - 1);\n break;\n case \"Home\":\n move(0);\n break;\n case \"End\":\n move(items.length - 1);\n break;\n default:\n return;\n }\n event.preventDefault();\n }\n document.addEventListener(\"keydown\", onKeyDown);\n return () => document.removeEventListener(\"keydown\", onKeyDown);\n }, [current, items.length, move, onClose, open]);\n\n useEffect(() => {\n if (!open || typeof Image === \"undefined\") return;\n for (const offset of [-1, 1]) {\n const neighbour = items[(current + offset + items.length) % items.length];\n if (!neighbour) continue;\n const preload = new Image();\n preload.src = neighbour.src;\n }\n }, [current, items, open]);\n\n if (!open || items.length === 0) return null;\n\n const item = items[Math.max(0, Math.min(current, items.length - 1))];\n const withThumbnails = showThumbnails ?? items.length > 1;\n const multiple = items.length > 1;\n\n return (\n <Portal>\n <div\n ref={containerRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={item.alt}\n className={cn(styles.overlay, className)}\n >\n <div className={styles.topBar}>\n {showCounter && multiple ? (\n <span className={styles.counter}>\n {current + 1} / {items.length}\n </span>\n ) : (\n <span />\n )}\n <button\n type=\"button\"\n className={styles.iconButton}\n aria-label={closeLabel}\n onClick={onClose}\n >\n <X size={20} />\n </button>\n </div>\n\n <div className={styles.stage}>\n {multiple ? (\n <button\n type=\"button\"\n className={cn(styles.iconButton, styles.nav)}\n aria-label={previousLabel}\n disabled={!loop && current === 0}\n onClick={() => move(current - 1)}\n >\n <ChevronLeft size={24} />\n </button>\n ) : null}\n\n <figure className={styles.figure}>\n <img src={item.src} alt={item.alt} className={styles.image} />\n {item.caption ? (\n <figcaption className={styles.caption}>{item.caption}</figcaption>\n ) : null}\n </figure>\n\n {multiple ? (\n <button\n type=\"button\"\n className={cn(styles.iconButton, styles.nav)}\n aria-label={nextLabel}\n disabled={!loop && current === items.length - 1}\n onClick={() => move(current + 1)}\n >\n <ChevronRight size={24} />\n </button>\n ) : null}\n </div>\n\n {withThumbnails && multiple ? (\n <div className={styles.thumbnails} role=\"tablist\" aria-label={item.alt}>\n {items.map((entry, entryIndex) => (\n <button\n key={`${entry.src}-${entryIndex}`}\n type=\"button\"\n role=\"tab\"\n aria-selected={entryIndex === current}\n aria-label={entry.alt}\n className={cn(\n styles.thumbnail,\n entryIndex === current && styles.thumbnailActive,\n )}\n onClick={() => move(entryIndex)}\n >\n <img\n src={entry.thumbnail ?? entry.src}\n alt=\"\"\n className={styles.thumbnailImage}\n />\n </button>\n ))}\n </div>\n ) : null}\n </div>\n </Portal>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ var e=`tempest_overlay_ZZFy5`,t=`tempest_overlay-in_xSy6S`,n=`tempest_topBar_KFfyo`,r=`tempest_counter_tVBEK`,i=`tempest_iconButton_475l0`,a=`tempest_stage_MkXLd`,o=`tempest_nav_9nijD`,s=`tempest_figure_pZz5C`,c=`tempest_image_fAx-S`,l=`tempest_caption_yB3pA`,u=`tempest_thumbnails_wROsn`,d=`tempest_thumbnail_Vyp-R`,f=`tempest_thumbnailActive_ucTff`,p=`tempest_thumbnailImage_mvEYb`,m={overlay:e,overlayIn:t,topBar:n,counter:r,iconButton:i,stage:a,nav:o,figure:s,image:c,caption:l,thumbnails:u,thumbnail:d,thumbnailActive:f,thumbnailImage:p};exports.caption=l,exports.counter=r,exports.default=m,exports.figure=s,exports.iconButton=i,exports.image=c,exports.nav=o,exports.overlay=e,exports.overlayIn=t,exports.stage=a,exports.thumbnail=d,exports.thumbnailActive=f,exports.thumbnailImage=p,exports.thumbnails=u,exports.topBar=n;
2
+ //# sourceMappingURL=Lightbox.module.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Lightbox.module.cjs","names":[],"sources":["../../../src/components/Lightbox/Lightbox.module.css"],"sourcesContent":[".overlay {\n position: fixed;\n inset: 0;\n z-index: var(--tempest-z-modal);\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-3);\n padding: var(--tempest-space-3);\n padding-top: max(var(--tempest-space-3), var(--tempest-safe-area-top));\n padding-bottom: max(var(--tempest-space-3), var(--tempest-safe-area-bottom));\n background-color: rgb(0 0 0 / 0.88);\n animation: overlay-in var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n@keyframes overlay-in {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n\n.topBar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--tempest-space-3);\n flex: 0 0 auto;\n}\n\n.counter {\n color: #ffffff;\n font-size: var(--tempest-text-sm);\n font-variant-numeric: tabular-nums;\n}\n\n.iconButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border: 0;\n border-radius: var(--tempest-radius-full);\n background-color: rgb(255 255 255 / 0.12);\n color: #ffffff;\n cursor: pointer;\n transition: background-color var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.iconButton:hover:not(:disabled) {\n background-color: rgb(255 255 255 / 0.24);\n}\n\n.iconButton:disabled {\n opacity: 0.35;\n cursor: not-allowed;\n}\n\n.iconButton:focus-visible {\n outline: var(--tempest-focus-ring-width) solid #ffffff;\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n.stage {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-space-3);\n flex: 1 1 auto;\n min-height: 0;\n}\n\n.nav {\n flex: 0 0 auto;\n}\n\n.figure {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--tempest-space-2);\n margin: 0;\n min-width: 0;\n min-height: 0;\n}\n\n.image {\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n border-radius: var(--tempest-radius-sm);\n}\n\n.caption {\n color: rgb(255 255 255 / 0.82);\n font-size: var(--tempest-text-sm);\n text-align: center;\n}\n\n.thumbnails {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-space-2);\n flex: 0 0 auto;\n overflow-x: auto;\n overscroll-behavior-x: contain;\n padding-bottom: var(--tempest-space-1);\n}\n\n.thumbnail {\n flex: 0 0 auto;\n width: 56px;\n height: 56px;\n padding: 0;\n border: 2px solid transparent;\n border-radius: var(--tempest-radius-sm);\n background: none;\n cursor: pointer;\n opacity: 0.55;\n transition:\n opacity var(--tempest-duration-fast) var(--tempest-ease-out),\n border-color var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.thumbnail:hover {\n opacity: 0.85;\n}\n\n.thumbnailActive {\n opacity: 1;\n border-color: #ffffff;\n}\n\n.thumbnail:focus-visible {\n outline: var(--tempest-focus-ring-width) solid #ffffff;\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n.thumbnailImage {\n width: 100%;\n height: 100%;\n object-fit: cover;\n border-radius: calc(var(--tempest-radius-sm) - 1px);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .overlay {\n animation: none;\n }\n\n .iconButton,\n .thumbnail {\n transition: none;\n }\n}\n"],"mappings":""}
@@ -0,0 +1,21 @@
1
+ //#region src/components/Lightbox/Lightbox.module.css
2
+ var e = "tempest_overlay_ZZFy5", t = "tempest_overlay-in_xSy6S", n = "tempest_topBar_KFfyo", r = "tempest_counter_tVBEK", i = "tempest_iconButton_475l0", a = "tempest_stage_MkXLd", o = "tempest_nav_9nijD", s = "tempest_figure_pZz5C", c = "tempest_image_fAx-S", l = "tempest_caption_yB3pA", u = "tempest_thumbnails_wROsn", d = "tempest_thumbnail_Vyp-R", f = "tempest_thumbnailActive_ucTff", p = "tempest_thumbnailImage_mvEYb", m = {
3
+ overlay: e,
4
+ overlayIn: t,
5
+ topBar: n,
6
+ counter: r,
7
+ iconButton: i,
8
+ stage: a,
9
+ nav: o,
10
+ figure: s,
11
+ image: c,
12
+ caption: l,
13
+ thumbnails: u,
14
+ thumbnail: d,
15
+ thumbnailActive: f,
16
+ thumbnailImage: p
17
+ };
18
+ //#endregion
19
+ export { l as caption, r as counter, m as default, s as figure, i as iconButton, c as image, o as nav, e as overlay, t as overlayIn, a as stage, d as thumbnail, f as thumbnailActive, p as thumbnailImage, u as thumbnails, n as topBar };
20
+
21
+ //# sourceMappingURL=Lightbox.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Lightbox.module.js","names":[],"sources":["../../../src/components/Lightbox/Lightbox.module.css"],"sourcesContent":[".overlay {\n position: fixed;\n inset: 0;\n z-index: var(--tempest-z-modal);\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-3);\n padding: var(--tempest-space-3);\n padding-top: max(var(--tempest-space-3), var(--tempest-safe-area-top));\n padding-bottom: max(var(--tempest-space-3), var(--tempest-safe-area-bottom));\n background-color: rgb(0 0 0 / 0.88);\n animation: overlay-in var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n@keyframes overlay-in {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n\n.topBar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--tempest-space-3);\n flex: 0 0 auto;\n}\n\n.counter {\n color: #ffffff;\n font-size: var(--tempest-text-sm);\n font-variant-numeric: tabular-nums;\n}\n\n.iconButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border: 0;\n border-radius: var(--tempest-radius-full);\n background-color: rgb(255 255 255 / 0.12);\n color: #ffffff;\n cursor: pointer;\n transition: background-color var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.iconButton:hover:not(:disabled) {\n background-color: rgb(255 255 255 / 0.24);\n}\n\n.iconButton:disabled {\n opacity: 0.35;\n cursor: not-allowed;\n}\n\n.iconButton:focus-visible {\n outline: var(--tempest-focus-ring-width) solid #ffffff;\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n.stage {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-space-3);\n flex: 1 1 auto;\n min-height: 0;\n}\n\n.nav {\n flex: 0 0 auto;\n}\n\n.figure {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--tempest-space-2);\n margin: 0;\n min-width: 0;\n min-height: 0;\n}\n\n.image {\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n border-radius: var(--tempest-radius-sm);\n}\n\n.caption {\n color: rgb(255 255 255 / 0.82);\n font-size: var(--tempest-text-sm);\n text-align: center;\n}\n\n.thumbnails {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-space-2);\n flex: 0 0 auto;\n overflow-x: auto;\n overscroll-behavior-x: contain;\n padding-bottom: var(--tempest-space-1);\n}\n\n.thumbnail {\n flex: 0 0 auto;\n width: 56px;\n height: 56px;\n padding: 0;\n border: 2px solid transparent;\n border-radius: var(--tempest-radius-sm);\n background: none;\n cursor: pointer;\n opacity: 0.55;\n transition:\n opacity var(--tempest-duration-fast) var(--tempest-ease-out),\n border-color var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.thumbnail:hover {\n opacity: 0.85;\n}\n\n.thumbnailActive {\n opacity: 1;\n border-color: #ffffff;\n}\n\n.thumbnail:focus-visible {\n outline: var(--tempest-focus-ring-width) solid #ffffff;\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n.thumbnailImage {\n width: 100%;\n height: 100%;\n object-fit: cover;\n border-radius: calc(var(--tempest-radius-sm) - 1px);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .overlay {\n animation: none;\n }\n\n .iconButton,\n .thumbnail {\n transition: none;\n }\n}\n"],"mappings":""}
@@ -0,0 +1,2 @@
1
+ const e=require("../../utils/cn.cjs"),t=require("../Button/Button.cjs"),n=require("./SignaturePad.module.cjs");let r=require("react"),i=require("react/jsx-runtime");var a=(0,r.forwardRef)(function({width:a=400,height:o=160,penColor:s,penWidth:c=2,disabled:l=!1,label:u=`Signature`,onBegin:d,onEnd:f,onEmptyChange:p,showActions:m=!0,clearLabel:h=`Clear`,undoLabel:g=`Undo`,className:_},v){let y=(0,r.useRef)(null),b=(0,r.useRef)([]),x=(0,r.useRef)(!1),[S,C]=(0,r.useState)(!0),w=(0,r.useCallback)(()=>{if(s)return s;let e=y.current;return!e||typeof window>`u`?`#101828`:window.getComputedStyle(e).color||`#101828`},[s]),T=(0,r.useCallback)(()=>{let e=y.current,t=e?.getContext(`2d`);if(!e||!t)return;let n=typeof window>`u`?1:window.devicePixelRatio||1;e.width=a*n,e.height=o*n,t.setTransform(n,0,0,n,0,0),t.clearRect(0,0,a,o),t.lineWidth=c,t.lineCap=`round`,t.lineJoin=`round`,t.strokeStyle=w();for(let e of b.current)if(e.length!==0){t.beginPath(),t.moveTo(e[0].x,e[0].y);for(let n of e.slice(1))t.lineTo(n.x,n.y);e.length===1&&t.lineTo(e[0].x,e[0].y),t.stroke()}},[o,c,w,a]);(0,r.useEffect)(()=>{T()},[T]);let E=(0,r.useCallback)(e=>{C(t=>(t!==e&&p?.(e),e))},[p]),D=(0,r.useCallback)(e=>{let t=y.current?.getBoundingClientRect();return{x:e.clientX-(t?.left??0),y:e.clientY-(t?.top??0)}},[]),O=(0,r.useCallback)(e=>{l||(x.current=!0,b.current=[...b.current,[D(e)]],y.current?.setPointerCapture?.(e.pointerId),d?.(),E(!1),T())},[l,d,D,T,E]),k=(0,r.useCallback)(e=>{if(!x.current||l)return;let t=b.current;t[t.length-1].push(D(e)),T()},[l,D,T]),A=(0,r.useCallback)(e=>{x.current&&(x.current=!1,y.current?.releasePointerCapture?.(e.pointerId),f?.(y.current?.toDataURL()??``))},[f]),j=(0,r.useCallback)(()=>{b.current=[],E(!0),T()},[T,E]),M=(0,r.useCallback)(()=>{b.current=b.current.slice(0,-1),E(b.current.length===0),T()},[T,E]);return(0,r.useImperativeHandle)(v,()=>({clear:j,undo:M,isEmpty:()=>b.current.length===0,toDataURL:(e,t)=>y.current?.toDataURL(e,t)??``,toBlob:(e,t)=>new Promise(n=>{let r=y.current;if(!r?.toBlob){n(null);return}r.toBlob(e=>n(e),e,t)})}),[j,M]),(0,i.jsxs)(`div`,{className:e.cn(n.default.pad,l&&n.default.disabled,_),children:[(0,i.jsx)(`canvas`,{ref:y,role:`img`,"aria-label":u,"aria-disabled":l||void 0,className:n.default.canvas,style:{width:a,height:o,touchAction:`none`},onPointerDown:O,onPointerMove:k,onPointerUp:A,onPointerLeave:A}),m?(0,i.jsxs)(`div`,{className:n.default.actions,children:[(0,i.jsx)(t.Button,{type:`button`,variant:`ghost`,size:`sm`,onClick:M,disabled:l||S,children:g}),(0,i.jsx)(t.Button,{type:`button`,variant:`ghost`,size:`sm`,onClick:j,disabled:l||S,children:h})]}):null]})});exports.SignaturePad=a;
2
+ //# sourceMappingURL=SignaturePad.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SignaturePad.cjs","names":[],"sources":["../../../src/components/SignaturePad/SignaturePad.tsx"],"sourcesContent":["import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from \"react\";\nimport type { PointerEvent as ReactPointerEvent } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { Button } from \"../Button\";\nimport styles from \"./SignaturePad.module.css\";\n\n/** A single sampled point of a stroke, in CSS pixels relative to the canvas. */\ninterface Point {\n x: number;\n y: number;\n}\n\n/** Imperative handle exposed through `ref`. */\nexport interface SignaturePadHandle {\n /** Drop every stroke. */\n clear: () => void;\n /** Drop the last stroke. */\n undo: () => void;\n /** `true` while nothing has been drawn. */\n isEmpty: () => boolean;\n /** Export as a data URL. Returns `\"\"` when the canvas is unavailable. */\n toDataURL: (type?: string, quality?: number) => string;\n /** Export as a `Blob` — what you actually upload. `null` when unavailable. */\n toBlob: (type?: string, quality?: number) => Promise<Blob | null>;\n}\n\nexport interface SignaturePadProps {\n /** Drawing surface width in CSS pixels. Default `400`. */\n width?: number;\n /** Drawing surface height in CSS pixels. Default `160`. */\n height?: number;\n /**\n * Stroke color. Defaults to the canvas' computed `color`, which the\n * stylesheet binds to `--tempest-text` — so the signature follows the theme\n * (including dark mode) instead of being hardcoded black.\n */\n penColor?: string;\n /** Stroke width in CSS pixels. Default `2`. */\n penWidth?: number;\n /** Blocks drawing and dims the surface. */\n disabled?: boolean;\n /** Accessible name of the surface. Default `\"Signature\"`. */\n label?: string;\n /** Called on the first move of each stroke. */\n onBegin?: () => void;\n /** Called when a stroke ends, with the current image as a data URL. */\n onEnd?: (dataUrl: string) => void;\n /** Called whenever the emptiness changes — wire it to a submit button. */\n onEmptyChange?: (isEmpty: boolean) => void;\n /** Renders the Clear/Undo buttons. Default `true`. */\n showActions?: boolean;\n clearLabel?: string;\n undoLabel?: string;\n className?: string;\n}\n\n/**\n * Signature capture on a canvas — the \"sign here\" field of a delivery receipt,\n * a service order, a term of acceptance.\n *\n * Strokes are kept as point lists and the canvas is **redrawn** from them, which\n * is what makes `undo` possible at all: a canvas holds pixels, not history, so\n * removing the last stroke means replaying the rest.\n *\n * The backing store is scaled by `devicePixelRatio`, so the line is crisp on a\n * phone instead of the blurry 1x bitmap a naive canvas produces.\n *\n * @example\n * ```tsx\n * const pad = useRef<SignaturePadHandle>(null);\n * const [empty, setEmpty] = useState(true);\n *\n * async function submit() {\n * const blob = await pad.current?.toBlob(\"image/png\");\n * if (blob) await api.upload(\"/receipts/42/signature\", blob);\n * }\n *\n * <SignaturePad ref={pad} label=\"Assinatura do cliente\" onEmptyChange={setEmpty} />\n * <Button disabled={empty} onClick={submit}>Enviar</Button>\n * ```\n */\nexport const SignaturePad = forwardRef<SignaturePadHandle, SignaturePadProps>(function SignaturePad(\n {\n width = 400,\n height = 160,\n penColor,\n penWidth = 2,\n disabled = false,\n label = \"Signature\",\n onBegin,\n onEnd,\n onEmptyChange,\n showActions = true,\n clearLabel = \"Clear\",\n undoLabel = \"Undo\",\n className,\n },\n ref,\n) {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const strokesRef = useRef<Point[][]>([]);\n const drawingRef = useRef(false);\n const [isEmpty, setIsEmpty] = useState(true);\n\n const resolvePenColor = useCallback((): string => {\n if (penColor) return penColor;\n const canvas = canvasRef.current;\n if (!canvas || typeof window === \"undefined\") return \"#101828\";\n return window.getComputedStyle(canvas).color || \"#101828\";\n }, [penColor]);\n\n /**\n * Repaint the whole surface from the stroke list, honouring the pixel ratio.\n *\n * A one-point stroke (a tap) gets a line back to its own origin: `stroke()`\n * over a path with a single point paints nothing, so without it a tap would\n * silently produce no dot while still counting as \"not empty\".\n */\n const redraw = useCallback((): void => {\n const canvas = canvasRef.current;\n const context = canvas?.getContext(\"2d\");\n if (!canvas || !context) return;\n\n const ratio = typeof window === \"undefined\" ? 1 : window.devicePixelRatio || 1;\n canvas.width = width * ratio;\n canvas.height = height * ratio;\n context.setTransform(ratio, 0, 0, ratio, 0, 0);\n context.clearRect(0, 0, width, height);\n\n context.lineWidth = penWidth;\n context.lineCap = \"round\";\n context.lineJoin = \"round\";\n context.strokeStyle = resolvePenColor();\n\n for (const stroke of strokesRef.current) {\n if (stroke.length === 0) continue;\n context.beginPath();\n context.moveTo(stroke[0].x, stroke[0].y);\n for (const point of stroke.slice(1)) {\n context.lineTo(point.x, point.y);\n }\n if (stroke.length === 1) context.lineTo(stroke[0].x, stroke[0].y);\n context.stroke();\n }\n }, [height, penWidth, resolvePenColor, width]);\n\n useEffect(() => {\n redraw();\n }, [redraw]);\n\n const setEmptiness = useCallback(\n (next: boolean): void => {\n setIsEmpty((current) => {\n if (current !== next) onEmptyChange?.(next);\n return next;\n });\n },\n [onEmptyChange],\n );\n\n const pointFrom = useCallback((event: ReactPointerEvent<HTMLCanvasElement>): Point => {\n const rect = canvasRef.current?.getBoundingClientRect();\n return {\n x: event.clientX - (rect?.left ?? 0),\n y: event.clientY - (rect?.top ?? 0),\n };\n }, []);\n\n const handlePointerDown = useCallback(\n (event: ReactPointerEvent<HTMLCanvasElement>): void => {\n if (disabled) return;\n drawingRef.current = true;\n strokesRef.current = [...strokesRef.current, [pointFrom(event)]];\n canvasRef.current?.setPointerCapture?.(event.pointerId);\n onBegin?.();\n setEmptiness(false);\n redraw();\n },\n [disabled, onBegin, pointFrom, redraw, setEmptiness],\n );\n\n const handlePointerMove = useCallback(\n (event: ReactPointerEvent<HTMLCanvasElement>): void => {\n if (!drawingRef.current || disabled) return;\n const strokes = strokesRef.current;\n strokes[strokes.length - 1].push(pointFrom(event));\n redraw();\n },\n [disabled, pointFrom, redraw],\n );\n\n const handlePointerUp = useCallback(\n (event: ReactPointerEvent<HTMLCanvasElement>): void => {\n if (!drawingRef.current) return;\n drawingRef.current = false;\n canvasRef.current?.releasePointerCapture?.(event.pointerId);\n onEnd?.(canvasRef.current?.toDataURL() ?? \"\");\n },\n [onEnd],\n );\n\n const clear = useCallback((): void => {\n strokesRef.current = [];\n setEmptiness(true);\n redraw();\n }, [redraw, setEmptiness]);\n\n const undo = useCallback((): void => {\n strokesRef.current = strokesRef.current.slice(0, -1);\n setEmptiness(strokesRef.current.length === 0);\n redraw();\n }, [redraw, setEmptiness]);\n\n useImperativeHandle(\n ref,\n () => ({\n clear,\n undo,\n isEmpty: () => strokesRef.current.length === 0,\n toDataURL: (type, quality) => canvasRef.current?.toDataURL(type, quality) ?? \"\",\n toBlob: (type, quality) =>\n new Promise((resolve) => {\n const canvas = canvasRef.current;\n if (!canvas?.toBlob) {\n resolve(null);\n return;\n }\n canvas.toBlob((blob) => resolve(blob), type, quality);\n }),\n }),\n [clear, undo],\n );\n\n return (\n <div className={cn(styles.pad, disabled && styles.disabled, className)}>\n <canvas\n ref={canvasRef}\n role=\"img\"\n aria-label={label}\n aria-disabled={disabled || undefined}\n className={styles.canvas}\n style={{ width, height, touchAction: \"none\" }}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n onPointerLeave={handlePointerUp}\n />\n {showActions ? (\n <div className={styles.actions}>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={undo}\n disabled={disabled || isEmpty}\n >\n {undoLabel}\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={clear}\n disabled={disabled || isEmpty}\n >\n {clearLabel}\n </Button>\n </div>\n ) : null}\n </div>\n );\n});\n"],"mappings":"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"}