@usefidel/design-system 0.4.0 → 0.6.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.6.0",
14
+ "resolvedSha": "e08dca273c717cf832900a20e846f8e5e955a1e3"
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,30 @@ 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
+ "data-highlighted": element.highlighted ? "true" : void 0,
220
+ "data-anchor-id": element.anchorId,
221
+ onMouseEnter: onHover ? () => onHover(index, true) : void 0,
222
+ onMouseLeave: onHover ? () => onHover(index, false) : void 0,
223
+ children: [
224
+ hasDetail ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
225
+ "button",
226
+ {
227
+ type: "button",
228
+ className: "fds-diff-section__element-head fds-diff-section__element-head--button",
229
+ "aria-expanded": open,
230
+ onClick: toggle,
231
+ children: lines
232
+ }
233
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fds-diff-section__element-head", children: lines }),
234
+ hasDetail && open && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fds-diff-section__detail", children: element.detail })
235
+ ]
236
+ }
237
+ );
204
238
  }
205
239
  function Chevron2({ open, className }) {
206
240
  const classes = [
package/dist/index.css CHANGED
@@ -310,6 +310,11 @@
310
310
  .fds-diff-section__element {
311
311
  display: flex;
312
312
  flex-direction: column;
313
+ transition: background var(--duration-quick, 150ms) ease;
314
+ }
315
+ .fds-diff-section__element[data-highlighted=true] {
316
+ background: var(--color-accent-subtle, var(--color-surface-hover));
317
+ box-shadow: inset 2px 0 0 var(--color-accent);
313
318
  }
314
319
  .fds-diff-section__element-head {
315
320
  display: flex;
package/dist/index.d.cts CHANGED
@@ -82,8 +82,28 @@ 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;
94
+ /**
95
+ * Emphasise this row without expanding it. When `true` the element renders a
96
+ * highlight treatment (a subtle fill and an accent edge). Use it to reflect
97
+ * an external selection — e.g. hovering a screenshot annotation highlighting
98
+ * its matching drawer row. Purely visual; does not affect `open`.
99
+ */
100
+ highlighted?: boolean;
101
+ /**
102
+ * Stamped onto the element root as `data-anchor-id`, giving the caller a
103
+ * stable handle to scroll this row into view (e.g. from a screenshot
104
+ * annotation) without threading a ref through the component.
105
+ */
106
+ anchorId?: string;
87
107
  }
88
108
  interface DiffSectionProps extends HTMLAttributes<HTMLDivElement> {
89
109
  /** Ordinal shown before the name, e.g. "01". Omitted in `headerless` mode. */
@@ -105,6 +125,14 @@ interface DiffSectionProps extends HTMLAttributes<HTMLDivElement> {
105
125
  * `mismatchCount` are ignored. @default false
106
126
  */
107
127
  headerless?: boolean;
128
+ /**
129
+ * Called when an element with `detail` is toggled. `index` is the element's
130
+ * position in `elements`. Pair with `DiffElement.open` to control expansion
131
+ * from the parent (e.g. to sync a screenshot highlight with drawer selection).
132
+ */
133
+ onElementToggle?: (index: number, open: boolean) => void;
134
+ /** Called when the pointer enters (`true`) or leaves (`false`) an element row. */
135
+ onElementHover?: (index: number, hovering: boolean) => void;
108
136
  }
109
137
  /**
110
138
  * Fidel design-system DiffSection — the mismatch drawer's section.
@@ -121,6 +149,6 @@ interface DiffSectionProps extends HTMLAttributes<HTMLDivElement> {
121
149
  * Presentational only — no diff taxonomy. Framing is Live → Design (founder
122
150
  * decision 2026-04-24): each delta reads `live → design`.
123
151
  */
124
- declare function DiffSection({ number, name, mismatchCount, elements, defaultExpanded, headerless, className, ...rest }: DiffSectionProps): react.JSX.Element;
152
+ declare function DiffSection({ number, name, mismatchCount, elements, defaultExpanded, headerless, onElementToggle, onElementHover, className, ...rest }: DiffSectionProps): react.JSX.Element;
125
153
 
126
154
  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,28 @@ 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;
94
+ /**
95
+ * Emphasise this row without expanding it. When `true` the element renders a
96
+ * highlight treatment (a subtle fill and an accent edge). Use it to reflect
97
+ * an external selection — e.g. hovering a screenshot annotation highlighting
98
+ * its matching drawer row. Purely visual; does not affect `open`.
99
+ */
100
+ highlighted?: boolean;
101
+ /**
102
+ * Stamped onto the element root as `data-anchor-id`, giving the caller a
103
+ * stable handle to scroll this row into view (e.g. from a screenshot
104
+ * annotation) without threading a ref through the component.
105
+ */
106
+ anchorId?: string;
87
107
  }
88
108
  interface DiffSectionProps extends HTMLAttributes<HTMLDivElement> {
89
109
  /** Ordinal shown before the name, e.g. "01". Omitted in `headerless` mode. */
@@ -105,6 +125,14 @@ interface DiffSectionProps extends HTMLAttributes<HTMLDivElement> {
105
125
  * `mismatchCount` are ignored. @default false
106
126
  */
107
127
  headerless?: boolean;
128
+ /**
129
+ * Called when an element with `detail` is toggled. `index` is the element's
130
+ * position in `elements`. Pair with `DiffElement.open` to control expansion
131
+ * from the parent (e.g. to sync a screenshot highlight with drawer selection).
132
+ */
133
+ onElementToggle?: (index: number, open: boolean) => void;
134
+ /** Called when the pointer enters (`true`) or leaves (`false`) an element row. */
135
+ onElementHover?: (index: number, hovering: boolean) => void;
108
136
  }
109
137
  /**
110
138
  * Fidel design-system DiffSection — the mismatch drawer's section.
@@ -121,6 +149,6 @@ interface DiffSectionProps extends HTMLAttributes<HTMLDivElement> {
121
149
  * Presentational only — no diff taxonomy. Framing is Live → Design (founder
122
150
  * decision 2026-04-24): each delta reads `live → design`.
123
151
  */
124
- declare function DiffSection({ number, name, mismatchCount, elements, defaultExpanded, headerless, className, ...rest }: DiffSectionProps): react.JSX.Element;
152
+ declare function DiffSection({ number, name, mismatchCount, elements, defaultExpanded, headerless, onElementToggle, onElementHover, className, ...rest }: DiffSectionProps): react.JSX.Element;
125
153
 
126
154
  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,30 @@ 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
+ "data-highlighted": element.highlighted ? "true" : void 0,
192
+ "data-anchor-id": element.anchorId,
193
+ onMouseEnter: onHover ? () => onHover(index, true) : void 0,
194
+ onMouseLeave: onHover ? () => onHover(index, false) : void 0,
195
+ children: [
196
+ hasDetail ? /* @__PURE__ */ jsx3(
197
+ "button",
198
+ {
199
+ type: "button",
200
+ className: "fds-diff-section__element-head fds-diff-section__element-head--button",
201
+ "aria-expanded": open,
202
+ onClick: toggle,
203
+ children: lines
204
+ }
205
+ ) : /* @__PURE__ */ jsx3("div", { className: "fds-diff-section__element-head", children: lines }),
206
+ hasDetail && open && /* @__PURE__ */ jsx3("div", { className: "fds-diff-section__detail", children: element.detail })
207
+ ]
208
+ }
209
+ );
176
210
  }
177
211
  function Chevron2({ open, className }) {
178
212
  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.6.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",