@lessly/ui 2.0.0 → 2.1.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.
- package/dist/index.d.ts +32 -23
- package/dist/index.js +47 -45
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -429,27 +429,25 @@ declare const Slider: React$1.ForwardRefExoticComponent<Omit<SliderPrimitive.Sli
|
|
|
429
429
|
/**
|
|
430
430
|
* Two signals on two channels that never touch (#446), and two treatments for two shapes.
|
|
431
431
|
*
|
|
432
|
-
* **Fill means the pointer is here
|
|
433
|
-
*
|
|
432
|
+
* **Fill means the pointer is here — except inside a track, where it means "this one".**
|
|
433
|
+
* `ToggleGroup` picks the treatment from its `type`, the question being "can nothing be selected?".
|
|
434
434
|
*
|
|
435
435
|
* - `frame` (a standalone `Toggle`, and every `type="multiple"` group): each item carries its own
|
|
436
436
|
* 1px hairline and selection steps it up to `border-strong` — 5.17:1 against the page in light and
|
|
437
|
-
* 5.47:1 in dark, and 3.63:1 / 4.31:1 above the resting hairline it replaces.
|
|
438
|
-
*
|
|
439
|
-
* - `track` (every `type="single"` group): the
|
|
440
|
-
*
|
|
441
|
-
* 1.
|
|
442
|
-
*
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
*
|
|
446
|
-
*
|
|
447
|
-
*
|
|
448
|
-
*
|
|
449
|
-
*
|
|
450
|
-
*
|
|
451
|
-
* 1.11:1 against the page in dark and 1.14:1 in light, and `shadow-sm` is the one dark step
|
|
452
|
-
* `@lessly/tokens@0.7.0` leaves without a rim, so in dark it can only darken.
|
|
437
|
+
* 5.47:1 in dark, and 3.63:1 / 4.31:1 above the resting hairline it replaces. Nothing is filled but
|
|
438
|
+
* the hovered item, so a filter row still says which of its filters are applied.
|
|
439
|
+
* - `track` (every `type="single"` group): the selected slot is a `bg-brand-subtle` tint with its
|
|
440
|
+
* label in `text-brand` at 600 against the unselected 500. No edge on the slot and no frame around
|
|
441
|
+
* the set. The tint measures 1.104:1 against the page in light and 1.144:1 in dark; the selected
|
|
442
|
+
* word measures 1.55:1 light and 1.26:1 against the unselected word — 1.19:1 and 1.35:1 with hue
|
|
443
|
+
* removed — and 5.89:1 light / 5.07:1 dark against the tint it sits on. The weight step is the one
|
|
444
|
+
* channel that survives hue removal.
|
|
445
|
+
*
|
|
446
|
+
* The `frame` mark is a **ring**, not a border: a ring is a box-shadow, so it costs no layout and an
|
|
447
|
+
* icon-only toggle stays square at the height its `size` declares. `track` draws no ring and no
|
|
448
|
+
* `shadow-sm`: `shadow-sm` is the one dark step `@lessly/tokens@0.7.0` leaves without a rim, so in
|
|
449
|
+
* dark it can only darken, and its blur composites to 1.11:1 against the page in dark and 1.14:1 in
|
|
450
|
+
* light.
|
|
453
451
|
*
|
|
454
452
|
* Focus is an **outline held 2px off the box** — a flush ring painted over the selection mark, and
|
|
455
453
|
* focused-selected and focused-unselected measured 1.55:1 apart, which is #446 on the keyboard.
|
|
@@ -477,12 +475,14 @@ declare const Toggle: React$1.ForwardRefExoticComponent<Omit<TogglePrimitive.Tog
|
|
|
477
475
|
*
|
|
478
476
|
* `multiple` is a set of independent chips — a filter row, where nothing need be on — so each chip
|
|
479
477
|
* carries its own edge and selection steps that edge up. `single` is one object with a moving part,
|
|
480
|
-
* so it takes the segmented-control treatment (#346)
|
|
481
|
-
* frame with no fill — and the selected slot is the only filled thing in the row.
|
|
478
|
+
* so it takes the segmented-control treatment (#346).
|
|
482
479
|
*
|
|
483
|
-
* The
|
|
484
|
-
*
|
|
485
|
-
*
|
|
480
|
+
* **The selected slot is a tint and a heavier word, and the track draws nothing else.** The
|
|
481
|
+
* `bg-brand-subtle` fill measures 1.104:1 against the page in light and 1.144:1 in dark; the label
|
|
482
|
+
* goes to `text-brand` at 600 against the unselected 500, which is 1.55:1 light and 1.26:1 dark
|
|
483
|
+
* against the unselected word — 1.19:1 and 1.35:1 with hue removed, so the weight step is the one
|
|
484
|
+
* channel that does not depend on seeing colour. There is no edge on the slot and no frame around
|
|
485
|
+
* the set: down a stacked list a frame put a rounded rectangle around every row.
|
|
486
486
|
*/
|
|
487
487
|
declare const ToggleGroup: React$1.ForwardRefExoticComponent<((Omit<ToggleGroupPrimitive.ToggleGroupSingleProps & React$1.RefAttributes<HTMLDivElement>, "ref"> | Omit<ToggleGroupPrimitive.ToggleGroupMultipleProps & React$1.RefAttributes<HTMLDivElement>, "ref">) & VariantProps<(props?: ({
|
|
488
488
|
treatment?: "track" | "frame" | null | undefined;
|
|
@@ -1389,6 +1389,15 @@ interface ExtensionIconProps extends LucideProps {
|
|
|
1389
1389
|
/**
|
|
1390
1390
|
* Renders the lucide-react icon named by a kebab-case manifest id, falling back
|
|
1391
1391
|
* to the Puzzle glyph (plus a one-time dev warning) for unknown ids.
|
|
1392
|
+
*
|
|
1393
|
+
* A fallback carries `data-icon-fallback="<the unresolved id>"`, in every
|
|
1394
|
+
* environment. That is the contract a consumer gates on — one DOM query in a
|
|
1395
|
+
* snapshot, an E2E assertion, or a grep over built output names both the fact
|
|
1396
|
+
* and the id, without a throw and without a line of production console output:
|
|
1397
|
+
*
|
|
1398
|
+
* ```ts
|
|
1399
|
+
* expect(container.querySelector('[data-icon-fallback]')).toBeNull();
|
|
1400
|
+
* ```
|
|
1392
1401
|
*/
|
|
1393
1402
|
declare const ExtensionIcon: React$1.ForwardRefExoticComponent<Omit<ExtensionIconProps, "ref"> & React$1.RefAttributes<SVGSVGElement>>;
|
|
1394
1403
|
|
package/dist/index.js
CHANGED
|
@@ -158,7 +158,13 @@ var DialogContent = React3.forwardRef(({ className, children, showClose = true,
|
|
|
158
158
|
{
|
|
159
159
|
ref,
|
|
160
160
|
className: cn(
|
|
161
|
-
|
|
161
|
+
// The gap at both screen edges is an inset, not a narrower width: `inset-x-4 w-auto` sets
|
|
162
|
+
// the two edges and `mx-auto` centres whatever `max-w-lg` leaves over. Capping the width
|
|
163
|
+
// instead — a `max-w-[calc(100vw-2rem)]` beside `max-w-lg` — is the same tailwind-merge
|
|
164
|
+
// group, so it evicts the 512px cap, and a caller's own `max-w-2xl` would take the gap
|
|
165
|
+
// with it. The 16 is `AppShell`'s own mobile inset (#337), so this edge lands on the edge
|
|
166
|
+
// of the page behind the overlay.
|
|
167
|
+
"fixed inset-x-4 top-[50%] z-50 mx-auto grid w-auto max-w-lg translate-y-[-50%] gap-4 rounded-lg border border-border-subtle bg-bg-elevated p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
162
168
|
className
|
|
163
169
|
),
|
|
164
170
|
...props,
|
|
@@ -842,7 +848,7 @@ import * as TogglePrimitive from "@radix-ui/react-toggle";
|
|
|
842
848
|
import { cva as cva3 } from "class-variance-authority";
|
|
843
849
|
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
844
850
|
var toggleVariants = cva3(
|
|
845
|
-
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium text-text-secondary transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-border-focus disabled:pointer-events-none disabled:opacity-50
|
|
851
|
+
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium text-text-secondary transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-border-focus disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
846
852
|
{
|
|
847
853
|
variants: {
|
|
848
854
|
/**
|
|
@@ -850,8 +856,8 @@ var toggleVariants = cva3(
|
|
|
850
856
|
* lone toggle is by definition something that may be off.
|
|
851
857
|
*/
|
|
852
858
|
treatment: {
|
|
853
|
-
frame: "ring-1 ring-inset ring-border-subtle hover:bg-bg-secondary hover:text-text-primary data-[state=on]:ring-border-strong",
|
|
854
|
-
track: "data-[state=off]:hover:
|
|
859
|
+
frame: "ring-1 ring-inset ring-border-subtle hover:bg-bg-secondary hover:text-text-primary data-[state=on]:text-text-primary data-[state=on]:ring-border-strong",
|
|
860
|
+
track: "data-[state=off]:hover:text-text-primary data-[state=on]:bg-bg-brand-subtle data-[state=on]:text-text-brand"
|
|
855
861
|
},
|
|
856
862
|
variant: {
|
|
857
863
|
default: "bg-transparent",
|
|
@@ -903,8 +909,8 @@ var SlidingThumb = ({ index, count }) => /* @__PURE__ */ jsx20(
|
|
|
903
909
|
"span",
|
|
904
910
|
{
|
|
905
911
|
"aria-hidden": "true",
|
|
906
|
-
className: "motion-segment-thumb pointer-events-none absolute inset-y-0
|
|
907
|
-
style: { width: `calc(
|
|
912
|
+
className: "motion-segment-thumb pointer-events-none absolute inset-y-0 left-0 rounded-md bg-bg-brand-subtle",
|
|
913
|
+
style: { width: `calc(100% / ${count})`, transform: `translateX(${index * 100}%)` }
|
|
908
914
|
}
|
|
909
915
|
);
|
|
910
916
|
var ToggleGroup = React16.forwardRef(({ className, variant, size, treatment, sliding, children, ...props }, ref) => {
|
|
@@ -929,8 +935,10 @@ var ToggleGroup = React16.forwardRef(({ className, variant, size, treatment, sli
|
|
|
929
935
|
className: cn(
|
|
930
936
|
resolved === "track" ? (
|
|
931
937
|
// `w-fit` because a flex or grid parent blockifies `inline-flex` and then stretches it:
|
|
932
|
-
// measured, the track spanned the full 1400px inside Storybook's column.
|
|
933
|
-
|
|
938
|
+
// measured, the track spanned the full 1400px inside Storybook's column. Nothing is
|
|
939
|
+
// drawn here: no frame, no fill, and so no padding to hold slots off a frame — which is
|
|
940
|
+
// what lets the thumb be exactly `100% / count` wide and travel in multiples of itself.
|
|
941
|
+
"inline-flex w-fit items-center gap-0.5"
|
|
934
942
|
) : "flex items-center justify-center gap-1",
|
|
935
943
|
// A sliding track is as wide as what holds it and its slots share that width equally, because
|
|
936
944
|
// the thumb is one slot wide and travels in multiples of itself. The gap goes for the same
|
|
@@ -960,12 +968,12 @@ var ToggleGroupItem = React16.forwardRef(({ className, children, variant, size,
|
|
|
960
968
|
// the height its size declares. A slot is the one place this matters most — the items of a
|
|
961
969
|
// group sit side by side, so one that is 8px wider than its neighbours is visible.
|
|
962
970
|
isIconOnly(children) && ICON_ONLY_BOX,
|
|
963
|
-
// Inside a track, the selected slot is also the heavier word: weight is the one channel
|
|
964
|
-
//
|
|
971
|
+
// Inside a track, the selected slot is also the heavier word: weight is the one channel that
|
|
972
|
+
// survives hue removal, and the tint under it is 1.104:1 light / 1.144:1 dark on its own.
|
|
965
973
|
context.treatment === "track" && "data-[state=on]:font-semibold",
|
|
966
|
-
// Under a sliding thumb the
|
|
967
|
-
//
|
|
968
|
-
context.sliding && "relative z-10 flex-1 data-[state=on]:bg-transparent
|
|
974
|
+
// Under a sliding thumb the tint belongs to the thumb, which is the one that travels; a second
|
|
975
|
+
// copy on the slot would sit still while the first one moved. The word stays the slot's own.
|
|
976
|
+
context.sliding && "relative z-10 flex-1 data-[state=on]:bg-transparent"
|
|
969
977
|
);
|
|
970
978
|
if (inert) {
|
|
971
979
|
return /* @__PURE__ */ jsx20("span", { ref, "aria-hidden": "true", className: cn(box, "invisible", className), children });
|
|
@@ -1158,7 +1166,13 @@ var AlertDialogContent = React21.forwardRef(({ className, ...props }, ref) => /*
|
|
|
1158
1166
|
{
|
|
1159
1167
|
ref,
|
|
1160
1168
|
className: cn(
|
|
1161
|
-
|
|
1169
|
+
// The gap at both screen edges is an inset, not a narrower width: `inset-x-4 w-auto` sets
|
|
1170
|
+
// the two edges and `mx-auto` centres whatever `max-w-lg` leaves over. Capping the width
|
|
1171
|
+
// instead — a `max-w-[calc(100vw-2rem)]` beside `max-w-lg` — is the same tailwind-merge
|
|
1172
|
+
// group, so it evicts the 512px cap, and a caller's own `max-w-2xl` would take the gap
|
|
1173
|
+
// with it. The 16 is `AppShell`'s own mobile inset (#337), so this edge lands on the edge
|
|
1174
|
+
// of the page behind the overlay.
|
|
1175
|
+
"fixed inset-x-4 top-[50%] z-50 mx-auto grid w-auto max-w-lg translate-y-[-50%] gap-4 rounded-lg border border-border-subtle bg-bg-elevated p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
1162
1176
|
className
|
|
1163
1177
|
),
|
|
1164
1178
|
...props
|
|
@@ -3426,7 +3440,7 @@ var ExtensionIcon = React51.forwardRef(
|
|
|
3426
3440
|
const Icon = resolveIcon(name);
|
|
3427
3441
|
if (!Icon) {
|
|
3428
3442
|
warnUnknownIconOnce(name);
|
|
3429
|
-
return /* @__PURE__ */ jsx62(Puzzle, { ref, ...props });
|
|
3443
|
+
return /* @__PURE__ */ jsx62(Puzzle, { ref, ...props, "data-icon-fallback": name });
|
|
3430
3444
|
}
|
|
3431
3445
|
return /* @__PURE__ */ jsx62(Icon, { ref, ...props });
|
|
3432
3446
|
}
|
|
@@ -6058,25 +6072,15 @@ function ThresholdControl({
|
|
|
6058
6072
|
onChange
|
|
6059
6073
|
}) {
|
|
6060
6074
|
return /* @__PURE__ */ jsx95(
|
|
6061
|
-
|
|
6075
|
+
ToggleGroup,
|
|
6062
6076
|
{
|
|
6063
|
-
|
|
6077
|
+
type: "single",
|
|
6078
|
+
size: "sm",
|
|
6064
6079
|
"aria-label": `Interrupt me for ${label}`,
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
type: "button",
|
|
6070
|
-
"aria-pressed": value === threshold,
|
|
6071
|
-
onClick: () => onChange(threshold),
|
|
6072
|
-
className: cn(
|
|
6073
|
-
"rounded-[7px] border border-transparent px-2.5 py-1 text-sm transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-border-focus",
|
|
6074
|
-
value === threshold ? "border-border-selected bg-bg-selected font-semibold text-text-primary" : "text-text-secondary hover:text-text-primary"
|
|
6075
|
-
),
|
|
6076
|
-
children: text
|
|
6077
|
-
},
|
|
6078
|
-
threshold
|
|
6079
|
-
))
|
|
6080
|
+
value,
|
|
6081
|
+
onValueChange: (next) => next && onChange(next),
|
|
6082
|
+
className: "shrink-0",
|
|
6083
|
+
children: THRESHOLDS.map(([threshold, text]) => /* @__PURE__ */ jsx95(ToggleGroupItem, { value: threshold, children: text }, threshold))
|
|
6080
6084
|
}
|
|
6081
6085
|
);
|
|
6082
6086
|
}
|
|
@@ -6138,20 +6142,18 @@ var NotificationSettings = React79.forwardRef(
|
|
|
6138
6142
|
return /* @__PURE__ */ jsxs63("div", { ref, className: cn("space-y-5", className), children: [
|
|
6139
6143
|
products && products.length > 0 && /* @__PURE__ */ jsxs63("div", { children: [
|
|
6140
6144
|
/* @__PURE__ */ jsx95("p", { className: "mb-2 px-0.5 text-xs font-bold uppercase tracking-wider text-text-tertiary", children: "Preferences for" }),
|
|
6141
|
-
/* @__PURE__ */ jsx95(
|
|
6142
|
-
|
|
6145
|
+
/* @__PURE__ */ jsx95(
|
|
6146
|
+
ToggleGroup,
|
|
6143
6147
|
{
|
|
6144
|
-
type: "
|
|
6145
|
-
"
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
),
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
t.id
|
|
6154
|
-
)) })
|
|
6148
|
+
type: "single",
|
|
6149
|
+
size: "sm",
|
|
6150
|
+
"aria-label": "Preferences for",
|
|
6151
|
+
value: tab,
|
|
6152
|
+
onValueChange: (next) => next && setTab(next),
|
|
6153
|
+
className: "flex-wrap",
|
|
6154
|
+
children: [{ id: DEFAULT_KEY, label: "Default" }, ...products].map((t) => /* @__PURE__ */ jsx95(ToggleGroupItem, { value: t.id, children: t.label }, t.id))
|
|
6155
|
+
}
|
|
6156
|
+
)
|
|
6155
6157
|
] }),
|
|
6156
6158
|
isDefault ? /* @__PURE__ */ jsxs63(Fragment11, { children: [
|
|
6157
6159
|
/* @__PURE__ */ jsx95("p", { className: "text-sm leading-relaxed text-text-secondary", children: "Your defaults, applied to every product unless you customize one. Everything addressed to you always lands in the in-app record; that cannot be turned off." }),
|