@usefragments/ui 1.3.2 → 1.3.3

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usefragments/ui",
3
- "version": "1.3.2",
3
+ "version": "1.3.3",
4
4
  "license": "MIT",
5
5
  "description": "Customizable UI components built on Base UI headless primitives",
6
6
  "author": "Conan McNicholl",
@@ -241,7 +241,7 @@
241
241
  "vite": "^6.0.0",
242
242
  "vitest": "^2.1.8",
243
243
  "vitest-axe": "^0.1.0",
244
- "@usefragments/core": "1.4.0"
244
+ "@usefragments/core": "1.7.0"
245
245
  },
246
246
  "files": [
247
247
  "src",
@@ -249,24 +249,56 @@ function dedent(str: string): string {
249
249
  return lines.map((line) => line.slice(minIndent)).join("\n");
250
250
  }
251
251
 
252
+ /**
253
+ * Languages where leading whitespace carries meaning, so the first line has to
254
+ * take part in the common-indent calculation like any other.
255
+ */
256
+ const INDENTATION_SIGNIFICANT_LANGUAGES = new Set<CodeBlockLanguage>([
257
+ "yaml",
258
+ "yml",
259
+ "json",
260
+ "python",
261
+ "py",
262
+ ]);
263
+
252
264
  /**
253
265
  * Normalize indentation while handling JSX where first line is already at column 0.
266
+ *
267
+ * The JSX case this exists for is a snippet authored inline, where the body
268
+ * carries the source file's indentation but the opening tag does not:
269
+ *
270
+ * code={`<Card>
271
+ * <Card.Header>Title</Card.Header>
272
+ * </Card>`}
273
+ *
274
+ * Excluding line 0 from the minimum is what renders that correctly. But
275
+ * `normalizeCode` trims first, so line 0 has always had its indentation removed
276
+ * by the time we get here — which means the same rule strips one real level off
277
+ * any snippet with a single root and an indented body:
278
+ *
279
+ * job: job:
280
+ * image: node → image: node (now a sibling key, not a child)
281
+ *
282
+ * For YAML, JSON and Python that is not cosmetic: the rendered snippet is
283
+ * invalid, and copying it gives you a broken file. Those languages count line 0,
284
+ * which is plain dedent. Everything else keeps the JSX-friendly heuristic.
254
285
  */
255
- function normalizeIndentation(str: string): string {
286
+ function normalizeIndentation(str: string, language: CodeBlockLanguage): string {
256
287
  const lines = str.split("\n");
257
288
  if (lines.length <= 1) return str;
258
289
 
290
+ const firstLineCounts = INDENTATION_SIGNIFICANT_LANGUAGES.has(language);
259
291
  let minIndent = Infinity;
260
292
  const firstLineIndent = lines[0].match(/^(\s*)/)?.[1].length ?? 0;
261
293
 
262
- for (let i = 1; i < lines.length; i += 1) {
294
+ for (let i = firstLineCounts ? 0 : 1; i < lines.length; i += 1) {
263
295
  const line = lines[i];
264
296
  if (line.trim().length === 0) continue;
265
297
  const indent = line.match(/^(\s*)/)?.[1].length ?? 0;
266
298
  minIndent = Math.min(minIndent, indent);
267
299
  }
268
300
 
269
- if (firstLineIndent > 0) {
301
+ if (!firstLineCounts && firstLineIndent > 0) {
270
302
  minIndent = Math.min(minIndent, firstLineIndent);
271
303
  }
272
304
 
@@ -431,11 +463,11 @@ function formatLongJsxTags(code: string): string {
431
463
  .join("\n");
432
464
  }
433
465
 
434
- function normalizeCode(code: string): string {
466
+ function normalizeCode(code: string, language: CodeBlockLanguage): string {
435
467
  const trimmed = code.trim();
436
468
  if (trimmed.length === 0) return "";
437
469
 
438
- const normalized = normalizeIndentation(trimmed);
470
+ const normalized = normalizeIndentation(trimmed, language);
439
471
  const dedented = dedent(normalized);
440
472
  const withoutTrailingWhitespace = trimTrailingWhitespace(dedented);
441
473
  return formatLongJsxTags(withoutTrailingWhitespace);
@@ -562,7 +594,7 @@ const CodeBlockBase = React.forwardRef<HTMLDivElement, CodeBlockProps>(function
562
594
  const [highlight, setHighlight] = useState<{ html: string; loading: boolean }>({ html: '', loading: true });
563
595
  const [isCollapsed, setIsCollapsed] = useState(defaultCollapsed);
564
596
 
565
- const trimmedCode = useMemo(() => normalizeCode(code), [code]);
597
+ const trimmedCode = useMemo(() => normalizeCode(code, language), [code, language]);
566
598
  const codeLines = trimmedCode.split("\n");
567
599
  const totalLines = codeLines.length;
568
600
  const shouldShowCollapse = collapsible && totalLines > collapsedLines;
@@ -2,6 +2,11 @@
2
2
  @use "../../tokens/mixins" as *;
3
3
 
4
4
  .wrapper {
5
+ // Tables may have a larger intrinsic width than a narrow flex or grid
6
+ // column. Keep that width inside this scroll container instead of allowing
7
+ // the container itself to widen the page.
8
+ min-width: 0;
9
+ max-width: 100%;
5
10
  --fui-data-table-bg: var(--fui-table-bg, var(--fui-bg-elevated, #{$fui-bg-elevated}));
6
11
  --fui-data-table-header-bg: var(
7
12
  --fui-table-header-bg,