@charcuterie/ui 4.27.0 → 4.27.1

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.
@@ -23,7 +23,7 @@ export const SegmentedOption = ({ isChecked, isFullWidth, item, onChoose, regist
23
23
  }
24
24
  return registerFocus(value);
25
25
  }, [isDisabled, registerFocus, value]);
26
- return (_jsx("button", { "aria-checked": isChecked, className: toClassName("cursor-pointer rounded-sm border border-transparent font-medium tabular-nums transition-colors duration-(--duration-fast) ease-standard",
26
+ return (_jsx("button", { "aria-checked": isChecked, className: toClassName("inline-flex cursor-pointer items-center justify-center rounded-sm border border-transparent font-medium tabular-nums transition-colors duration-(--duration-fast) ease-standard",
27
27
  // In a very narrow board, three full labels cannot share
28
28
  // one row. The minimum lets two fit, then the final option
29
29
  // takes a full second row instead of wrapping its label or
@@ -1 +1 @@
1
- {"version":3,"file":"SegmentedOption.js","sourceRoot":"","sources":["../../src/SegmentedControl/SegmentedOption.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAoB/C;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,EAC9B,SAAS,EACT,WAAW,EACX,IAAI,EACJ,QAAQ,EACR,aAAa,EACb,iBAAiB,EACjB,IAAI,EACJ,QAAQ,EACR,YAAY,GACS,EAAa,EAAE;IACpC,MAAM,EAAE,UAAU,GAAG,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,IAAI,CAAA;IAEjD,SAAS,CACP,GAAG,EAAE,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAC9B,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAC3B,CAAA;IAED,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,UAAU,EAAE,CAAC;YACf,OAAM;QACR,CAAC;QAED,OAAO,aAAa,CAAC,KAAK,CAAC,CAAA;IAC7B,CAAC,EAAE,CAAC,UAAU,EAAE,aAAa,EAAE,KAAK,CAAC,CAAC,CAAA;IAEtC,OAAO,CAEL,iCACgB,SAAS,EACvB,SAAS,EAAE,WAAW,CACpB,yIAAyI;QACzI,yDAAyD;QACzD,2DAA2D;QAC3D,2DAA2D;QAC3D,kCAAkC;QAClC,WAAW,IAAI,0BAA0B,EACzC,kBAAkB,CAAC,IAAI,CAAC;QACxB,mDAAmD;QACnD,6DAA6D;QAC7D,0DAA0D;QAC1D,2CAA2C;QAC3C,SAAS;YACP,CAAC,CAAC,oDAAoD;YACtD,CAAC,CAAC,mFAAmF,EACvF,UAAU;YACR,2FAA2F,EAC7F,gBAAgB,CACjB,EACD,QAAQ,EAAE,UAAU,EACpB,OAAO,EAAE,GAAG,EAAE;YACZ,QAAQ,CAAC,KAAK,CAAC,CAAA;QACjB,CAAC,EACD,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE;YACf,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;QAC9B,CAAC,EACD,IAAI,EAAC,OAAO,EACZ,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAC,QAAQ,YAEZ,KAAK,GACC,CACV,CAAA;AACH,CAAC,CAAA"}
1
+ {"version":3,"file":"SegmentedOption.js","sourceRoot":"","sources":["../../src/SegmentedControl/SegmentedOption.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAoB/C;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,EAC9B,SAAS,EACT,WAAW,EACX,IAAI,EACJ,QAAQ,EACR,aAAa,EACb,iBAAiB,EACjB,IAAI,EACJ,QAAQ,EACR,YAAY,GACS,EAAa,EAAE;IACpC,MAAM,EAAE,UAAU,GAAG,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,IAAI,CAAA;IAEjD,SAAS,CACP,GAAG,EAAE,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAC9B,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAC3B,CAAA;IAED,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,UAAU,EAAE,CAAC;YACf,OAAM;QACR,CAAC;QAED,OAAO,aAAa,CAAC,KAAK,CAAC,CAAA;IAC7B,CAAC,EAAE,CAAC,UAAU,EAAE,aAAa,EAAE,KAAK,CAAC,CAAC,CAAA;IAEtC,OAAO,CAEL,iCACgB,SAAS,EACvB,SAAS,EAAE,WAAW,CACpB,iLAAiL;QACjL,yDAAyD;QACzD,2DAA2D;QAC3D,2DAA2D;QAC3D,kCAAkC;QAClC,WAAW,IAAI,0BAA0B,EACzC,kBAAkB,CAAC,IAAI,CAAC;QACxB,mDAAmD;QACnD,6DAA6D;QAC7D,0DAA0D;QAC1D,2CAA2C;QAC3C,SAAS;YACP,CAAC,CAAC,oDAAoD;YACtD,CAAC,CAAC,mFAAmF,EACvF,UAAU;YACR,2FAA2F,EAC7F,gBAAgB,CACjB,EACD,QAAQ,EAAE,UAAU,EACpB,OAAO,EAAE,GAAG,EAAE;YACZ,QAAQ,CAAC,KAAK,CAAC,CAAA;QACjB,CAAC,EACD,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE;YACf,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;QAC9B,CAAC,EACD,IAAI,EAAC,OAAO,EACZ,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAC,QAAQ,YAEZ,KAAK,GACC,CACV,CAAA;AACH,CAAC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@charcuterie/ui",
3
- "version": "4.27.0",
3
+ "version": "4.27.1",
4
4
  "description": "The Charcuterie components. Tokens for look, @charcuterie/logic for state, ARIA that agents can actually drive.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -200,3 +200,44 @@ export const FullWidth: Story = {
200
200
  export const Interactive: Story = {
201
201
  args: { items: COLUMN_ITEMS, label: "Columns" },
202
202
  }
203
+
204
+ /** Inline rich labels must be centered as boxes, not aligned to an anonymous text baseline. */
205
+ export const RichLabels: Story = {
206
+ args: {
207
+ isFullWidth: true,
208
+ label: "Message type",
209
+ items: [
210
+ {
211
+ value: "text",
212
+ label: (
213
+ <span className="inline-flex items-center gap-2 whitespace-nowrap">
214
+ <span
215
+ aria-hidden="true"
216
+ className="size-4 rounded-sm bg-current"
217
+ />
218
+ <span className="min-w-0 truncate">
219
+ Type a message
220
+ </span>
221
+ </span>
222
+ ),
223
+ },
224
+ {
225
+ value: "voice",
226
+ label: (
227
+ <span className="inline-flex items-center gap-2 whitespace-nowrap">
228
+ <span
229
+ aria-hidden="true"
230
+ className="size-4 rounded-full bg-current"
231
+ />
232
+ <span className="min-w-0 truncate">
233
+ Your voice
234
+ </span>
235
+ <small className="min-w-0 truncate text-xs">
236
+ Later
237
+ </small>
238
+ </span>
239
+ ),
240
+ },
241
+ ],
242
+ },
243
+ }
@@ -7,8 +7,43 @@ import { mountStory } from "../mountStory.testHelpers.ts"
7
7
  import { expectAgentDrivable } from "../testing/index.ts"
8
8
  import * as stories from "./SegmentedControl.stories.tsx"
9
9
 
10
- const { AllStates, Playground, FullWidth, Interactive } =
11
- composeStories(stories)
10
+ const {
11
+ AllStates,
12
+ Playground,
13
+ FullWidth,
14
+ Interactive,
15
+ RichLabels,
16
+ } = composeStories(stories)
17
+
18
+ test("rich label boxes are centered inside both segmented options", async () => {
19
+ const { canvas } = await mountStory(RichLabels)
20
+ const group = expectAgentDrivable(canvas, {
21
+ name: "Message type",
22
+ role: "radiogroup",
23
+ })
24
+ for (const option of getOptions(group)) {
25
+ const label = option.firstElementChild
26
+ if (!label) throw new Error("The rich label is missing")
27
+ const optionBox = option.getBoundingClientRect()
28
+ const labelBox = label.getBoundingClientRect()
29
+ expect(
30
+ Math.abs(
31
+ optionBox.y +
32
+ optionBox.height / 2 -
33
+ labelBox.y -
34
+ labelBox.height / 2,
35
+ ),
36
+ ).toBeLessThan(0.5)
37
+ expect(
38
+ Math.abs(
39
+ optionBox.x +
40
+ optionBox.width / 2 -
41
+ labelBox.x -
42
+ labelBox.width / 2,
43
+ ),
44
+ ).toBeLessThan(0.5)
45
+ }
46
+ })
12
47
 
13
48
  /**
14
49
  * `querySelectorAll` rather than `getAllByRole("radio")`, and not
@@ -237,7 +272,7 @@ test("full-width mode fills its container without wrapping labels", async () =>
237
272
  )
238
273
  expect(
239
274
  Math.max(...widths) - Math.min(...widths),
240
- ).toBeLessThan(1)
275
+ ).toBeLessThan(0.5)
241
276
  }
242
277
  }
243
278
  })
@@ -66,7 +66,7 @@ export const SegmentedOption = ({
66
66
  <button
67
67
  aria-checked={isChecked}
68
68
  className={toClassName(
69
- "cursor-pointer rounded-sm border border-transparent font-medium tabular-nums transition-colors duration-(--duration-fast) ease-standard",
69
+ "inline-flex cursor-pointer items-center justify-center rounded-sm border border-transparent font-medium tabular-nums transition-colors duration-(--duration-fast) ease-standard",
70
70
  // In a very narrow board, three full labels cannot share
71
71
  // one row. The minimum lets two fit, then the final option
72
72
  // takes a full second row instead of wrapping its label or