torch-glare 2.5.3 → 2.5.5

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 (56) hide show
  1. package/apps/lib/components/ActionButton.tsx +39 -6
  2. package/apps/lib/components/Badge.tsx +5 -2
  3. package/apps/lib/components/BadgeField.tsx +71 -67
  4. package/apps/lib/components/Button.tsx +4 -4
  5. package/apps/lib/components/Calendar.tsx +7 -17
  6. package/apps/lib/components/Card.tsx +2 -1
  7. package/apps/lib/components/ColorPicker.tsx +1 -1
  8. package/apps/lib/components/ContextMenu.tsx +65 -22
  9. package/apps/lib/components/DataViews/context.ts +2 -2
  10. package/apps/lib/components/DataViews/data-views.tsx +3 -3
  11. package/apps/lib/components/DataViews/filters/filters.tsx +0 -2
  12. package/apps/lib/components/DataViews/views/table-view.tsx +190 -153
  13. package/apps/lib/components/Drawer.tsx +18 -1
  14. package/apps/lib/components/DropdownMenu.tsx +65 -22
  15. package/apps/lib/components/FormBuilder/fields/FieldShell.tsx +19 -5
  16. package/apps/lib/components/FormBuilder/types.ts +16 -0
  17. package/apps/lib/components/FormRenderer/form-renderer.tsx +16 -5
  18. package/apps/lib/components/FormRenderer/stepper.tsx +22 -16
  19. package/apps/lib/components/HeaderBar.tsx +51 -53
  20. package/apps/lib/components/Input.tsx +19 -4
  21. package/apps/lib/components/InputField.tsx +46 -47
  22. package/apps/lib/components/Popover.tsx +108 -56
  23. package/apps/lib/components/SearchableSelect.tsx +22 -17
  24. package/apps/lib/components/SearchableTree.tsx +33 -17
  25. package/apps/lib/components/SearchableTreeDialog.tsx +21 -12
  26. package/apps/lib/components/SectionBlock.tsx +3 -1
  27. package/apps/lib/components/Select.tsx +86 -54
  28. package/apps/lib/components/SimpleSelect.tsx +2 -2
  29. package/apps/lib/components/SlideDatePicker.tsx +15 -4
  30. package/apps/lib/components/Stepper.tsx +329 -180
  31. package/apps/lib/components/Switch.tsx +2 -2
  32. package/apps/lib/components/TabSwitch.tsx +18 -12
  33. package/apps/lib/components/Table.tsx +1 -1
  34. package/apps/lib/components/Textarea.tsx +29 -4
  35. package/apps/lib/components/TreeDropDown.tsx +18 -6
  36. package/apps/lib/layouts/FieldSection.tsx +28 -2
  37. package/apps/lib/registry.json +7 -17
  38. package/apps/lib/tsconfig.tsbuildinfo +1 -1
  39. package/docs/components/action-button.md +3 -1
  40. package/docs/components/badge-field.md +4 -4
  41. package/docs/components/context-menu.md +3 -1
  42. package/docs/components/data-views/examples/filters.md +0 -1
  43. package/docs/components/data-views/index.md +1 -17
  44. package/docs/components/dropdown-menu.md +3 -0
  45. package/docs/components/form-builder.md +27 -1
  46. package/docs/components/header-bar.md +3 -2
  47. package/docs/components/input-field.md +3 -3
  48. package/docs/components/select.md +1 -1
  49. package/docs/components/stepper.md +119 -24
  50. package/docs/migration/changelog.md +13 -0
  51. package/docs/reference/tailwind-plugins.md +42 -0
  52. package/docs/tutorials/getting-started.md +32 -11
  53. package/package.json +1 -1
  54. package/apps/lib/components/DataViews/filters/summary.tsx +0 -65
  55. package/apps/lib/components/FormStepper.tsx +0 -272
  56. package/docs/components/form-stepper.md +0 -250
@@ -31,13 +31,16 @@ const trackStyles = cva(
31
31
  "rounded-[10px]",
32
32
  "bg-background-presentation-body-primary",
33
33
  "shadow-[inset_0_0_4px_0_rgba(0,0,0,0.08)]",
34
+ // Contains the active pill's drop-shadow: it is clipped at the track's rounded edge instead
35
+ // of bleeding onto whatever sits behind the switcher.
36
+ "overflow-hidden",
34
37
  ],
35
38
  {
36
39
  variants: {
37
40
  size: {
38
- S: ["gap-[2px] p-[2px]"],
39
- M: ["gap-[2px] p-[2px]"],
40
- L: ["gap-[3px] p-[3px]"],
41
+ S: ["p-[2px]"],
42
+ M: ["p-[2px]"],
43
+ L: ["p-[3px] rounded-[12px]"],
41
44
  },
42
45
  },
43
46
  defaultVariants: { size: "M" },
@@ -48,7 +51,7 @@ const trackStyles = cva(
48
51
  // transparent and lighten on hover.
49
52
  const optionStyles = cva(
50
53
  [
51
- "flex items-center justify-center gap-[4px]",
54
+ "flex items-center justify-center",
52
55
  "rounded-[8px]",
53
56
  "font-[510] leading-none",
54
57
  "transition-all duration-200 ease-in-out",
@@ -61,7 +64,7 @@ const optionStyles = cva(
61
64
  size: {
62
65
  S: ["h-5 px-2 text-[12px]", "[&_svg]:h-3 [&_svg]:w-3", "[&_i]:text-[12px]"],
63
66
  M: ["h-6 px-3 text-[14px]", "[&_svg]:h-[14px] [&_svg]:w-[14px]", "[&_i]:text-[14px]"],
64
- L: ["h-8 px-4 text-[16px]", "[&_svg]:h-4 [&_svg]:w-4", "[&_i]:text-[16px]"],
67
+ L: ["h-8 px-4 text-[16px] rounded-[10px]", "[&_svg]:h-4 [&_svg]:w-4", "[&_i]:text-[16px]"],
65
68
  },
66
69
  active: {
67
70
  // Active option = a solid raised WHITE pill with dark text, in every
@@ -78,6 +81,8 @@ const optionStyles = cva(
78
81
  // keeps both states the same width — without it, selecting a tab resizes the pill and
79
82
  // shoves its neighbours. The design's own 4px gap absorbs the 1px.
80
83
  "border border-black/5",
84
+ // The pill casts outward to look raised; the track clips it (`overflow-hidden` on
85
+ // `trackStyles`) so the shadow never spills past the container.
81
86
  "drop-shadow-[0px_0px_5px_rgba(0,0,0,0.25)]",
82
87
  ],
83
88
  false: [
@@ -116,22 +121,23 @@ function TabSwitchInner<T extends string = string>(
116
121
  >
117
122
  {options.map((option, idx) => {
118
123
  const active = option.value === value;
124
+ const isDisabled = disabled || option.disabled;
125
+ // A divider is drawn only between two INACTIVE options — the raised pill is never flanked
126
+ // by a rule, so the two dividers touching it fade out.
119
127
  const prevActive = idx > 0 && options[idx - 1].value === value;
120
- // A divider sits between two inactive options only — the active pill is
121
- // never flanked by one.
122
128
  const showDivider = idx > 0 && !active && !prevActive;
123
- const isDisabled = disabled || option.disabled;
124
129
 
125
130
  return (
126
131
  <div key={option.value} className="flex items-center">
127
132
  {idx > 0 && (
128
133
  <div
129
134
  aria-hidden
135
+ // The slot is ALWAYS rendered and only its ink changes. Unmounting it instead cost
136
+ // the track 7px (`w-px` + `mx-[3px]`) every time one went away, so picking a tab
137
+ // resized the track and shoved its neighbours — with three options, selecting the
138
+ // middle one dropped both dividers and moved the track 14px.
130
139
  className={cn(
131
- // The slot is always here, and only its ink changes. Unmounting it instead cost
132
- // the track 7px every time the rule below hid one, so picking a tab resized the
133
- // track and shoved its neighbours sideways.
134
- "mx-[3px] h-3 w-px",
140
+ "h-3 w-px",
135
141
  showDivider ? "bg-border-presentation-action-disabled" : "bg-transparent",
136
142
  )}
137
143
  />
@@ -32,7 +32,7 @@ const Table = React.forwardRef<
32
32
  // worked example.
33
33
  //
34
34
  // `[border-collapse:separate]` is what lets the header cells keep their borders while stuck.
35
- className={cn("overflow-hidden w-auto [border-collapse:separate] border-spacing-0", className)}
35
+ className={cn("overflow-visible w-auto [border-collapse:separate] border-spacing-0", className)}
36
36
  {...props}
37
37
  >
38
38
  {props.children}
@@ -7,12 +7,19 @@ import { cva, VariantProps } from "class-variance-authority";
7
7
  const textareaStyles = cva(
8
8
  [
9
9
  "border",
10
- "rounded-[8px]",
10
+ // `radius/xl` (12px). Figma's TextArea-Field has two sizes — S is `radius/lg` (8px) and M is
11
+ // `radius/xl` — and this component has no size variant: it renders its `Label` wrapper at
12
+ // `size="M"` below, so M is the size it actually is. Supporting S properly needs a real `size`
13
+ // prop, which is an API change rather than a radius one.
14
+ "rounded-radius-xl",
11
15
  "px-[8px]",
12
16
  "py-[12px]",
13
17
  "outline-none",
14
18
  "typography-body-large-regular",
15
- "!min-h-[36px]",
19
+ // A floor for the empty field — without one, `field-sizing: content` collapses it to a single
20
+ // 50px line. No `!` — twMerge does not dedupe an important class against a plain one, so `!`
21
+ // here would make the height impossible to override from a caller's `className`.
22
+ "min-h-[200px]",
16
23
  "transition-[border,background-color,color,caret-color,box-shadow]",
17
24
  "ease-in-out",
18
25
  "duration-150",
@@ -29,7 +36,7 @@ const textareaStyles = cva(
29
36
  "disabled:text-border-presentation-action-disabled",
30
37
  "disabled:cursor-not-allowed",
31
38
  "disabled:placeholder-border-presentation-action-disabled",
32
- "field-sizing-content w-full min-w-[100px] max-w-[100%]",
39
+ "w-full min-w-[100px] max-w-[100%]",
33
40
  ],
34
41
  {
35
42
  variants: {
@@ -54,6 +61,14 @@ interface TextareaProps
54
61
  secondaryLabel?: string; // Additional label text
55
62
  direction?: "row" | "column";
56
63
  theme?: "dark" | "light" | "default";
64
+ /**
65
+ * Grow the field to fit its content as the user types, instead of scrolling inside a fixed box.
66
+ * On by default, via CSS `field-sizing: content`. Set a `max-h-*` on the field to cap the growth —
67
+ * past the cap it scrolls. Note `rows` has no effect while this is on: the browser sizes the field
68
+ * from its content, so an empty field starts at one line. Pass `autoResize={false}` for the old
69
+ * fixed-height box with a drag handle.
70
+ */
71
+ autoResize?: boolean;
57
72
  }
58
73
 
59
74
  // Textarea component definition
@@ -68,6 +83,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
68
83
  direction = "row",
69
84
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
70
85
  theme, // excluded from ...props spread
86
+ autoResize = true,
71
87
  ...props
72
88
  },
73
89
  ref,
@@ -82,7 +98,16 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
82
98
  size={"M"}
83
99
  className={cn(className)}
84
100
  >
85
- <textarea className={cn(textareaStyles({ state }))} ref={ref} {...props} />
101
+ <textarea
102
+ // Auto-grow is pure CSS. Written as an arbitrary *property* rather than as
103
+ // `field-sizing-content`: that utility only exists in Tailwind v4 and this project is on
104
+ // v3, where it compiled to no rule at all and left the computed `field-sizing` at `fixed`.
105
+ // The drag handle goes away with it — the height is content-driven, and a manual drag
106
+ // would pin it and silently stop the growing.
107
+ className={cn(textareaStyles({ state }), autoResize && "[field-sizing:content] resize-none")}
108
+ ref={ref}
109
+ {...props}
110
+ />
86
111
  </Label>
87
112
  );
88
113
  },
@@ -6,9 +6,9 @@ import { HTMLAttributes, ReactNode, useEffect, useState } from "react";
6
6
  const TreeDropDownVariants = cva(
7
7
  [
8
8
  "cursor-pointer flex px-[6px] h-[40px] gap-2 justify-start items-center w-full",
9
- "text-content-system-global-primary border-l-[2px] rtl:border-r-[2px] border-transparent outline-none",
9
+ "text-content-system-global-primary border-s-[2px] border-transparent outline-none",
10
10
  "hover:bg-white-alpha-075 hover:border-black-300 hover:text-content-system-action-primary-hover hover:gap-[14px]",
11
- "rounded-r-[4px] text-start whitespace-nowrap transition-all duration-150 ease-in-out",
11
+ "rounded-e-[4px] text-start whitespace-nowrap transition-all duration-150 ease-in-out",
12
12
  ],
13
13
  {
14
14
  variants: {
@@ -20,19 +20,31 @@ const TreeDropDownVariants = cva(
20
20
  true: "hover:gap-[8px]",
21
21
  },
22
22
  },
23
+ // The active row is a fixed dark slab, so its colours are pinned locally rather than taken from
24
+ // the theme. Previously the fill was theme-invariant while the text was not: on a light-themed
25
+ // page the row rendered near-black with near-black text, and the label was unreadable. The
26
+ // values below are exactly what the dark sidebar already rendered, so its look is unchanged.
23
27
  compoundVariants: [
24
28
  {
25
29
  active: true,
26
30
  variant: "default",
27
31
  className: [
28
- "bg-background-system-action-primary-hover border-border-system-action-primary-hover [&_button]:bg-purple-alpha-15 hover:bg-background-system-action-primary-hover hover:border-border-system-action-primary-hover",
32
+ "bg-purple-1000 border-purple-600 [&_button]:bg-purple-alpha-15",
33
+ "hover:bg-purple-1000 hover:border-purple-600",
34
+ // Descendants carry their own theme-driven text colours (headings, badges), so the
35
+ // foreground has to be forced here or it follows the page instead of the slab.
36
+ "text-content-presentation-global-primary-light",
37
+ "[&_*]:!text-content-presentation-global-primary-light",
29
38
  ],
30
39
  },
31
40
  {
32
41
  active: true,
33
42
  variant: "secondary",
34
43
  className: [
35
- "bg-wavy-navy-1000 border-border-system-action-field-hover-selected [&_button]:bg-blue-sparkle-alpha-15 hover:bg-wavy-navy-1000 hover:border-border-system-action-field-hover-selected",
44
+ "bg-wavy-navy-1000 border-blue-sparkle-600 [&_button]:bg-blue-sparkle-alpha-15",
45
+ "hover:bg-wavy-navy-1000 hover:border-blue-sparkle-600",
46
+ "text-content-presentation-global-primary-light",
47
+ "[&_*]:!text-content-presentation-global-primary-light",
36
48
  ],
37
49
  },
38
50
  ],
@@ -86,14 +98,14 @@ export const TreeDropDown = ({
86
98
  </div>
87
99
  <div
88
100
  className={cn(
89
- "mt-0 pl-[22px] relative overflow-auto scrollbar-hide transition-all duration-500 ease-in-out",
101
+ "mt-0 ps-[22px] relative overflow-auto scrollbar-hide transition-all duration-500 ease-in-out",
90
102
  {
91
103
  "max-h-[20000px] mt-1": isActive,
92
104
  "max-h-0": !isActive,
93
105
  },
94
106
  )}
95
107
  >
96
- <span className="h-full w-[1px] bg-border-system-global-primary absolute left-[21px] top-0 rounded-sm z-10" />
108
+ <span className="h-full w-[1px] bg-border-system-global-primary absolute start-[21px] top-0 rounded-sm z-10" />
97
109
  <div className={cn("h-full flex flex-col gap-1 w-full", childrenContainerClassName)}>
98
110
  {props.children}
99
111
  </div>
@@ -27,6 +27,14 @@ export function FieldSection({
27
27
  childrenUnderLabel,
28
28
  ...props
29
29
  }: Props) {
30
+ // Which copy of `childrenUnderLabel` is visible, driven purely by the `@md` container
31
+ // breakpoint (this section's own width reaching 650px) — deliberately not by `direction`.
32
+ // Pinning it per-direction breaks the common case: `FormRenderer` forces `direction="vertical"`
33
+ // inside a drawer and FormBuilder puts the field's validation error in this slot, so a vertical
34
+ // form would strand every error under the label at any width.
35
+ const underLabelSlot = direction === "vertical" ? "hidden" : "hidden @md:block";
36
+ const underChildrenSlot = direction === "vertical" ? "block" : "block @md:hidden";
37
+
30
38
  return (
31
39
  <section
32
40
  {...props}
@@ -54,11 +62,29 @@ export function FieldSection({
54
62
  )}
55
63
 
56
64
  {secondaryLabel && <Label size={size} secondaryLabel={secondaryLabel} />}
57
- {childrenUnderLabel}
65
+
66
+ {/* Stacked (below `@md`): the label column IS the full width, so under the label is the
67
+ natural spot. Hidden once `@md` splits the row into two columns — the copy in the
68
+ children column takes over there. Exactly one of the two is ever displayed. */}
69
+ {childrenUnderLabel && (
70
+ <div data-slot="under-label" className={cn(underLabelSlot)}>
71
+ {childrenUnderLabel}
72
+ </div>
73
+ )}
58
74
  </div>
59
75
 
60
76
  {/* Flexible section that takes up the remaining space */}
61
- <div className="grid grid-cols-1 place-items-end gap-[12px]">{children}</div>
77
+ <div className="grid grid-cols-1 place-items-end gap-[12px]">
78
+ {children}
79
+
80
+ {/* Two-column (`@md` and up): the hint belongs under the control it describes, not
81
+ stranded at the bottom of the 350px label column. */}
82
+ {childrenUnderLabel && (
83
+ <div data-slot="under-children" className={cn("w-full", underChildrenSlot)}>
84
+ {childrenUnderLabel}
85
+ </div>
86
+ )}
87
+ </div>
62
88
  </div>
63
89
  </section>
64
90
  );
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.5.3",
2
+ "version": "2.5.5",
3
3
  "generatedBy": "scripts/bin/generateRegistry",
4
4
  "npmVersions": {
5
5
  "@dnd-kit/core": "^6.3.1",
@@ -186,7 +186,7 @@
186
186
  "react-day-picker"
187
187
  ],
188
188
  "registryDependencies": [
189
- "components/Button",
189
+ "components/ActionButton",
190
190
  "components/SimpleSelect",
191
191
  "utils/cn"
192
192
  ]
@@ -457,25 +457,13 @@
457
457
  "components/Button",
458
458
  "components/Drawer",
459
459
  "components/FormBuilder",
460
- "components/FormStepper",
461
460
  "components/HeaderBar",
462
461
  "components/SectionBlock",
462
+ "components/Stepper",
463
463
  "components/TabFormItem",
464
464
  "utils/cn"
465
465
  ]
466
466
  },
467
- {
468
- "name": "FormStepper",
469
- "type": "components",
470
- "path": "components/FormStepper.tsx",
471
- "npmDependencies": [
472
- "class-variance-authority"
473
- ],
474
- "registryDependencies": [
475
- "utils/cn",
476
- "utils/types"
477
- ]
478
- },
479
467
  {
480
468
  "name": "FormSummary",
481
469
  "type": "components",
@@ -714,6 +702,7 @@
714
702
  "class-variance-authority"
715
703
  ],
716
704
  "registryDependencies": [
705
+ "components/ActionButton",
717
706
  "components/Button",
718
707
  "components/DropdownMenu",
719
708
  "components/Input",
@@ -743,6 +732,7 @@
743
732
  "path": "components/SearchableTree.tsx",
744
733
  "npmDependencies": [],
745
734
  "registryDependencies": [
735
+ "components/ActionButton",
746
736
  "components/Button",
747
737
  "components/Input",
748
738
  "components/Popover",
@@ -757,6 +747,7 @@
757
747
  "path": "components/SearchableTreeDialog.tsx",
758
748
  "npmDependencies": [],
759
749
  "registryDependencies": [
750
+ "components/ActionButton",
760
751
  "components/Button",
761
752
  "components/Dialog",
762
753
  "components/Input",
@@ -793,9 +784,8 @@
793
784
  "class-variance-authority"
794
785
  ],
795
786
  "registryDependencies": [
796
- "components/Button",
787
+ "components/ActionButton",
797
788
  "components/DropdownMenu",
798
- "components/Tooltip",
799
789
  "utils/cn",
800
790
  "utils/types"
801
791
  ]