@usefidel/design-system 0.4.0 → 0.5.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.
@@ -10,6 +10,6 @@
10
10
  "webapp.css"
11
11
  ],
12
12
  "storybookUrl": null,
13
- "packageVersion": "0.4.0",
14
- "resolvedSha": "2ffffb3224acff54fc34007bdbf831644f69fa22"
13
+ "packageVersion": "0.5.0",
14
+ "resolvedSha": "03a49c2e8d888b7916f449d7801daa922c525737"
15
15
  }
package/dist/index.cjs CHANGED
@@ -129,6 +129,8 @@ function DiffSection({
129
129
  elements,
130
130
  defaultExpanded = false,
131
131
  headerless = false,
132
+ onElementToggle,
133
+ onElementHover,
132
134
  className,
133
135
  ...rest
134
136
  }) {
@@ -165,14 +167,35 @@ function DiffSection({
165
167
  ]
166
168
  }
167
169
  ),
168
- open && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fds-diff-section__body", children: elements.map((el, ei) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ElementRow, { element: el }, `${el.name}-${ei}`)) })
170
+ open && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fds-diff-section__body", children: elements.map((el, ei) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
171
+ ElementRow,
172
+ {
173
+ element: el,
174
+ index: ei,
175
+ onToggle: onElementToggle,
176
+ onHover: onElementHover
177
+ },
178
+ `${el.name}-${ei}`
179
+ )) })
169
180
  ]
170
181
  }
171
182
  );
172
183
  }
173
- function ElementRow({ element }) {
184
+ function ElementRow({
185
+ element,
186
+ index,
187
+ onToggle,
188
+ onHover
189
+ }) {
174
190
  const hasDetail = element.detail != null;
175
- const [open, setOpen] = (0, import_react2.useState)(element.defaultOpen ?? false);
191
+ const isControlled = element.open !== void 0;
192
+ const [internalOpen, setInternalOpen] = (0, import_react2.useState)(element.defaultOpen ?? false);
193
+ const open = isControlled ? !!element.open : internalOpen;
194
+ const toggle = () => {
195
+ const next = !open;
196
+ if (!isControlled) setInternalOpen(next);
197
+ onToggle?.(index, next);
198
+ };
176
199
  const lines = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
177
200
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fds-diff-section__element-head-inner", children: [
178
201
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fds-diff-section__element-name", children: element.name }),
@@ -188,19 +211,28 @@ function ElementRow({ element }) {
188
211
  ] })
189
212
  ] }, `${line.property}-${li}`)) })
190
213
  ] });
191
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fds-diff-section__element", "data-open": hasDetail && open ? "true" : void 0, children: [
192
- hasDetail ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
193
- "button",
194
- {
195
- type: "button",
196
- className: "fds-diff-section__element-head fds-diff-section__element-head--button",
197
- "aria-expanded": open,
198
- onClick: () => setOpen((v) => !v),
199
- children: lines
200
- }
201
- ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fds-diff-section__element-head", children: lines }),
202
- hasDetail && open && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fds-diff-section__detail", children: element.detail })
203
- ] });
214
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
215
+ "div",
216
+ {
217
+ className: "fds-diff-section__element",
218
+ "data-open": hasDetail && open ? "true" : void 0,
219
+ onMouseEnter: onHover ? () => onHover(index, true) : void 0,
220
+ onMouseLeave: onHover ? () => onHover(index, false) : void 0,
221
+ children: [
222
+ hasDetail ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
223
+ "button",
224
+ {
225
+ type: "button",
226
+ className: "fds-diff-section__element-head fds-diff-section__element-head--button",
227
+ "aria-expanded": open,
228
+ onClick: toggle,
229
+ children: lines
230
+ }
231
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fds-diff-section__element-head", children: lines }),
232
+ hasDetail && open && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fds-diff-section__detail", children: element.detail })
233
+ ]
234
+ }
235
+ );
204
236
  }
205
237
  function Chevron2({ open, className }) {
206
238
  const classes = [
package/dist/index.d.cts CHANGED
@@ -82,8 +82,15 @@ interface DiffElement {
82
82
  * owns whatever lives inside.
83
83
  */
84
84
  detail?: ReactNode;
85
- /** When the element has `detail`, render it open on first paint. @default false */
85
+ /** When the element has `detail`, render it open on first paint (uncontrolled). @default false */
86
86
  defaultOpen?: boolean;
87
+ /**
88
+ * Controlled open state. When defined, the row is controlled: the component
89
+ * renders this value and never keeps its own expand state — the caller owns
90
+ * it via `onElementToggle` (so drawer selection can drive, e.g., a matching
91
+ * screenshot highlight). Leave undefined for uncontrolled (`defaultOpen`).
92
+ */
93
+ open?: boolean;
87
94
  }
88
95
  interface DiffSectionProps extends HTMLAttributes<HTMLDivElement> {
89
96
  /** Ordinal shown before the name, e.g. "01". Omitted in `headerless` mode. */
@@ -105,6 +112,14 @@ interface DiffSectionProps extends HTMLAttributes<HTMLDivElement> {
105
112
  * `mismatchCount` are ignored. @default false
106
113
  */
107
114
  headerless?: boolean;
115
+ /**
116
+ * Called when an element with `detail` is toggled. `index` is the element's
117
+ * position in `elements`. Pair with `DiffElement.open` to control expansion
118
+ * from the parent (e.g. to sync a screenshot highlight with drawer selection).
119
+ */
120
+ onElementToggle?: (index: number, open: boolean) => void;
121
+ /** Called when the pointer enters (`true`) or leaves (`false`) an element row. */
122
+ onElementHover?: (index: number, hovering: boolean) => void;
108
123
  }
109
124
  /**
110
125
  * Fidel design-system DiffSection — the mismatch drawer's section.
@@ -121,6 +136,6 @@ interface DiffSectionProps extends HTMLAttributes<HTMLDivElement> {
121
136
  * Presentational only — no diff taxonomy. Framing is Live → Design (founder
122
137
  * decision 2026-04-24): each delta reads `live → design`.
123
138
  */
124
- declare function DiffSection({ number, name, mismatchCount, elements, defaultExpanded, headerless, className, ...rest }: DiffSectionProps): react.JSX.Element;
139
+ declare function DiffSection({ number, name, mismatchCount, elements, defaultExpanded, headerless, onElementToggle, onElementHover, className, ...rest }: DiffSectionProps): react.JSX.Element;
125
140
 
126
141
  export { Button, type ButtonProps, type ButtonSize, type ButtonVariant, type DiffElement, type DiffPropertyLine, DiffSection, type DiffSectionProps, MismatchCard, type MismatchCardProps, type MismatchProperty };
package/dist/index.d.ts CHANGED
@@ -82,8 +82,15 @@ interface DiffElement {
82
82
  * owns whatever lives inside.
83
83
  */
84
84
  detail?: ReactNode;
85
- /** When the element has `detail`, render it open on first paint. @default false */
85
+ /** When the element has `detail`, render it open on first paint (uncontrolled). @default false */
86
86
  defaultOpen?: boolean;
87
+ /**
88
+ * Controlled open state. When defined, the row is controlled: the component
89
+ * renders this value and never keeps its own expand state — the caller owns
90
+ * it via `onElementToggle` (so drawer selection can drive, e.g., a matching
91
+ * screenshot highlight). Leave undefined for uncontrolled (`defaultOpen`).
92
+ */
93
+ open?: boolean;
87
94
  }
88
95
  interface DiffSectionProps extends HTMLAttributes<HTMLDivElement> {
89
96
  /** Ordinal shown before the name, e.g. "01". Omitted in `headerless` mode. */
@@ -105,6 +112,14 @@ interface DiffSectionProps extends HTMLAttributes<HTMLDivElement> {
105
112
  * `mismatchCount` are ignored. @default false
106
113
  */
107
114
  headerless?: boolean;
115
+ /**
116
+ * Called when an element with `detail` is toggled. `index` is the element's
117
+ * position in `elements`. Pair with `DiffElement.open` to control expansion
118
+ * from the parent (e.g. to sync a screenshot highlight with drawer selection).
119
+ */
120
+ onElementToggle?: (index: number, open: boolean) => void;
121
+ /** Called when the pointer enters (`true`) or leaves (`false`) an element row. */
122
+ onElementHover?: (index: number, hovering: boolean) => void;
108
123
  }
109
124
  /**
110
125
  * Fidel design-system DiffSection — the mismatch drawer's section.
@@ -121,6 +136,6 @@ interface DiffSectionProps extends HTMLAttributes<HTMLDivElement> {
121
136
  * Presentational only — no diff taxonomy. Framing is Live → Design (founder
122
137
  * decision 2026-04-24): each delta reads `live → design`.
123
138
  */
124
- declare function DiffSection({ number, name, mismatchCount, elements, defaultExpanded, headerless, className, ...rest }: DiffSectionProps): react.JSX.Element;
139
+ declare function DiffSection({ number, name, mismatchCount, elements, defaultExpanded, headerless, onElementToggle, onElementHover, className, ...rest }: DiffSectionProps): react.JSX.Element;
125
140
 
126
141
  export { Button, type ButtonProps, type ButtonSize, type ButtonVariant, type DiffElement, type DiffPropertyLine, DiffSection, type DiffSectionProps, MismatchCard, type MismatchCardProps, type MismatchProperty };
package/dist/index.js CHANGED
@@ -101,6 +101,8 @@ function DiffSection({
101
101
  elements,
102
102
  defaultExpanded = false,
103
103
  headerless = false,
104
+ onElementToggle,
105
+ onElementHover,
104
106
  className,
105
107
  ...rest
106
108
  }) {
@@ -137,14 +139,35 @@ function DiffSection({
137
139
  ]
138
140
  }
139
141
  ),
140
- open && /* @__PURE__ */ jsx3("div", { className: "fds-diff-section__body", children: elements.map((el, ei) => /* @__PURE__ */ jsx3(ElementRow, { element: el }, `${el.name}-${ei}`)) })
142
+ open && /* @__PURE__ */ jsx3("div", { className: "fds-diff-section__body", children: elements.map((el, ei) => /* @__PURE__ */ jsx3(
143
+ ElementRow,
144
+ {
145
+ element: el,
146
+ index: ei,
147
+ onToggle: onElementToggle,
148
+ onHover: onElementHover
149
+ },
150
+ `${el.name}-${ei}`
151
+ )) })
141
152
  ]
142
153
  }
143
154
  );
144
155
  }
145
- function ElementRow({ element }) {
156
+ function ElementRow({
157
+ element,
158
+ index,
159
+ onToggle,
160
+ onHover
161
+ }) {
146
162
  const hasDetail = element.detail != null;
147
- const [open, setOpen] = useState2(element.defaultOpen ?? false);
163
+ const isControlled = element.open !== void 0;
164
+ const [internalOpen, setInternalOpen] = useState2(element.defaultOpen ?? false);
165
+ const open = isControlled ? !!element.open : internalOpen;
166
+ const toggle = () => {
167
+ const next = !open;
168
+ if (!isControlled) setInternalOpen(next);
169
+ onToggle?.(index, next);
170
+ };
148
171
  const lines = /* @__PURE__ */ jsxs3(Fragment, { children: [
149
172
  /* @__PURE__ */ jsxs3("div", { className: "fds-diff-section__element-head-inner", children: [
150
173
  /* @__PURE__ */ jsx3("span", { className: "fds-diff-section__element-name", children: element.name }),
@@ -160,19 +183,28 @@ function ElementRow({ element }) {
160
183
  ] })
161
184
  ] }, `${line.property}-${li}`)) })
162
185
  ] });
163
- return /* @__PURE__ */ jsxs3("div", { className: "fds-diff-section__element", "data-open": hasDetail && open ? "true" : void 0, children: [
164
- hasDetail ? /* @__PURE__ */ jsx3(
165
- "button",
166
- {
167
- type: "button",
168
- className: "fds-diff-section__element-head fds-diff-section__element-head--button",
169
- "aria-expanded": open,
170
- onClick: () => setOpen((v) => !v),
171
- children: lines
172
- }
173
- ) : /* @__PURE__ */ jsx3("div", { className: "fds-diff-section__element-head", children: lines }),
174
- hasDetail && open && /* @__PURE__ */ jsx3("div", { className: "fds-diff-section__detail", children: element.detail })
175
- ] });
186
+ return /* @__PURE__ */ jsxs3(
187
+ "div",
188
+ {
189
+ className: "fds-diff-section__element",
190
+ "data-open": hasDetail && open ? "true" : void 0,
191
+ onMouseEnter: onHover ? () => onHover(index, true) : void 0,
192
+ onMouseLeave: onHover ? () => onHover(index, false) : void 0,
193
+ children: [
194
+ hasDetail ? /* @__PURE__ */ jsx3(
195
+ "button",
196
+ {
197
+ type: "button",
198
+ className: "fds-diff-section__element-head fds-diff-section__element-head--button",
199
+ "aria-expanded": open,
200
+ onClick: toggle,
201
+ children: lines
202
+ }
203
+ ) : /* @__PURE__ */ jsx3("div", { className: "fds-diff-section__element-head", children: lines }),
204
+ hasDetail && open && /* @__PURE__ */ jsx3("div", { className: "fds-diff-section__detail", children: element.detail })
205
+ ]
206
+ }
207
+ );
176
208
  }
177
209
  function Chevron2({ open, className }) {
178
210
  const classes = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usefidel/design-system",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Fidel brand design tokens + React component library — DTCG token source of truth, generated per-surface CSS + Tailwind-v4 parser-compatibility output, and on-brand React components (Button, MismatchCard, DiffSection).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",