@nextlyhq/ui 0.0.2-alpha.58 → 0.0.2-alpha.60
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.cjs +238 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +205 -71
- package/dist/index.d.ts +205 -71
- package/dist/index.mjs +234 -14
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/styles.scoped.css +1 -1
- package/dist/theme.css +10 -0
- package/package.json +6 -5
package/dist/index.mjs
CHANGED
|
@@ -291,7 +291,15 @@ var badgeVariants = cva3(
|
|
|
291
291
|
success: "bg-success-100 text-success-700 dark:bg-success-900 dark:text-success-100",
|
|
292
292
|
warning: "bg-warning-100 text-warning-700 dark:bg-warning-900 dark:text-warning-100",
|
|
293
293
|
destructive: "bg-destructive-100 text-destructive-700 dark:bg-destructive-900 dark:text-destructive-100",
|
|
294
|
-
|
|
294
|
+
/*
|
|
295
|
+
* The border colour is NOT important-marked. The base sets no border
|
|
296
|
+
* colour, so nothing inside this component competes for it — the only
|
|
297
|
+
* thing a mark could win against is the caller's own class. Seven
|
|
298
|
+
* outline badges pass one, including a status pill whose comment gives
|
|
299
|
+
* a 3:1 contrast reason for the colour it asks for, and every one of
|
|
300
|
+
* them was being overridden without any sign that it had been.
|
|
301
|
+
*/
|
|
302
|
+
outline: "border border-border bg-transparent text-foreground dark:text-muted-foreground dark:border-border"
|
|
295
303
|
}
|
|
296
304
|
},
|
|
297
305
|
defaultVariants: {
|
|
@@ -467,6 +475,13 @@ var COLS = {
|
|
|
467
475
|
4: "grid-cols-4",
|
|
468
476
|
6: "grid-cols-6"
|
|
469
477
|
};
|
|
478
|
+
var RESPONSIVE_COLS = {
|
|
479
|
+
1: "grid-cols-1",
|
|
480
|
+
2: "grid-cols-1 @2xl:grid-cols-2",
|
|
481
|
+
3: "grid-cols-1 @2xl:grid-cols-3",
|
|
482
|
+
4: "grid-cols-1 @2xl:grid-cols-4",
|
|
483
|
+
6: "grid-cols-1 @2xl:grid-cols-6"
|
|
484
|
+
};
|
|
470
485
|
var Stack = forwardRef7(
|
|
471
486
|
({ direction = "col", gap = 4, className, ...props }, ref) => /* @__PURE__ */ jsx10(
|
|
472
487
|
"div",
|
|
@@ -484,14 +499,20 @@ var Stack = forwardRef7(
|
|
|
484
499
|
);
|
|
485
500
|
Stack.displayName = "Stack";
|
|
486
501
|
var Grid = forwardRef7(
|
|
487
|
-
({ cols = 2, gap = 4, className, ...
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
502
|
+
({ cols = 2, gap = 4, responsive = false, className, children, ...rest }, ref) => {
|
|
503
|
+
if (!responsive) {
|
|
504
|
+
return /* @__PURE__ */ jsx10(
|
|
505
|
+
"div",
|
|
506
|
+
{
|
|
507
|
+
ref,
|
|
508
|
+
className: cn("grid", COLS[cols], GAP[gap], className),
|
|
509
|
+
...rest,
|
|
510
|
+
children
|
|
511
|
+
}
|
|
512
|
+
);
|
|
493
513
|
}
|
|
494
|
-
|
|
514
|
+
return /* @__PURE__ */ jsx10("div", { ref, className: cn("@container", className), ...rest, children: /* @__PURE__ */ jsx10("div", { className: cn("grid", RESPONSIVE_COLS[cols], GAP[gap]), children }) });
|
|
515
|
+
}
|
|
495
516
|
);
|
|
496
517
|
Grid.displayName = "Grid";
|
|
497
518
|
var Stat = forwardRef7(
|
|
@@ -958,7 +979,20 @@ var tabsListVariants = cva8(
|
|
|
958
979
|
// `gap-1` and `p-0` are invariant across the variants, so they belong here —
|
|
959
980
|
// repeating them in each arm gives any future adjustment two sites to change
|
|
960
981
|
// and one to forget.
|
|
961
|
-
|
|
982
|
+
//
|
|
983
|
+
// The rail the triggers sit on, and it is load-bearing rather than
|
|
984
|
+
// decoration. Each trigger draws a 2px border on its trailing edge and pulls
|
|
985
|
+
// itself half a step onto this one: with no rail here, that pull put a 2px
|
|
986
|
+
// line onto whatever followed the strip in the document, and above a rounded
|
|
987
|
+
// panel that is the corner curve — a straight bar crossing an 8px arc. The
|
|
988
|
+
// rail also gives an inactive tab somewhere to be inactive, which is what
|
|
989
|
+
// makes the active one read as selected rather than as the only tab.
|
|
990
|
+
//
|
|
991
|
+
// Which edge is the trailing one depends on orientation, so the rail follows
|
|
992
|
+
// it. Radix stamps `data-orientation` on the list and on every trigger, so
|
|
993
|
+
// both halves read the same source and cannot disagree about the axis. A
|
|
994
|
+
// fixed bottom rail draws a horizontal line beneath a vertical list.
|
|
995
|
+
"inline-flex items-center justify-center gap-1 rounded-none border-b border-border p-0 text-muted-foreground data-[orientation=vertical]:flex-col data-[orientation=vertical]:items-stretch data-[orientation=vertical]:border-b-0 data-[orientation=vertical]:border-r",
|
|
962
996
|
{
|
|
963
997
|
variants: { variant: TABS_LIST_VARIANTS },
|
|
964
998
|
defaultVariants: { variant: "default" }
|
|
@@ -979,9 +1013,22 @@ var TABS_TRIGGER_SIZES = {
|
|
|
979
1013
|
sm: "text-xs"
|
|
980
1014
|
};
|
|
981
1015
|
var tabsTriggerVariants = cva8(
|
|
982
|
-
// Square corners: the active state is a 2px
|
|
983
|
-
// full
|
|
984
|
-
|
|
1016
|
+
// Square corners: the active state is a 2px border on the trailing edge that
|
|
1017
|
+
// has to run the full length of the trigger and stay flush with its ends.
|
|
1018
|
+
//
|
|
1019
|
+
// The active colour is NOT important-marked. Nothing inside this component
|
|
1020
|
+
// competes for it — the inactive arm is mutually exclusive with the active
|
|
1021
|
+
// one — so the only thing an `!` could win against is a caller's class, which
|
|
1022
|
+
// is precisely what a theme supplies. Marking it made the one line under
|
|
1023
|
+
// every selected tab the one line a theme could not restyle, and the failure
|
|
1024
|
+
// read as "the theme did not apply here" rather than as an opt-out.
|
|
1025
|
+
//
|
|
1026
|
+
// The trailing edge is the bottom when the strip is horizontal and the right
|
|
1027
|
+
// when it is vertical, so the indicator, the half-step pull onto the rail and
|
|
1028
|
+
// the active colour all switch axis together off the same `data-orientation`
|
|
1029
|
+
// the list reads. Switching only some of them puts the selection affordance
|
|
1030
|
+
// on one axis and the line it sits on the other.
|
|
1031
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-none bg-transparent px-4 py-2 font-medium cursor-pointer transition-all duration-200 border-b-2 relative -mb-0.5 data-[state=active]:border-b-primary data-[state=active]:text-primary data-[state=inactive]:border-transparent data-[state=inactive]:text-muted-foreground hover:text-primary hover:border-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed data-[orientation=vertical]:border-b-0 data-[orientation=vertical]:mb-0 data-[orientation=vertical]:border-r-2 data-[orientation=vertical]:-mr-0.5 data-[orientation=vertical]:data-[state=active]:border-r-primary",
|
|
985
1032
|
{
|
|
986
1033
|
variants: { size: TABS_TRIGGER_SIZES },
|
|
987
1034
|
defaultVariants: { size: "default" }
|
|
@@ -1601,7 +1648,9 @@ import * as DialogPrimitive2 from "@radix-ui/react-dialog";
|
|
|
1601
1648
|
import { cva as cva11 } from "class-variance-authority";
|
|
1602
1649
|
import * as React7 from "react";
|
|
1603
1650
|
import { jsx as jsx26, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1604
|
-
var
|
|
1651
|
+
var SheetModalContext = React7.createContext(true);
|
|
1652
|
+
var Sheet = ({ modal = true, children, ...props }) => /* @__PURE__ */ jsx26(SheetModalContext.Provider, { value: modal, children: /* @__PURE__ */ jsx26(DialogPrimitive2.Root, { modal, ...props, children }) });
|
|
1653
|
+
Sheet.displayName = "Sheet";
|
|
1605
1654
|
var SheetTrigger = DialogPrimitive2.Trigger;
|
|
1606
1655
|
var SheetClose = DialogPrimitive2.Close;
|
|
1607
1656
|
var SheetPortal = DialogPrimitive2.Portal;
|
|
@@ -1642,8 +1691,9 @@ var sheetVariants = cva11(
|
|
|
1642
1691
|
);
|
|
1643
1692
|
var SheetContent = React7.forwardRef(({ side = "right", className, children, ...props }, ref) => {
|
|
1644
1693
|
const portalContainer = usePortalContainer();
|
|
1694
|
+
const modal = React7.useContext(SheetModalContext);
|
|
1645
1695
|
return /* @__PURE__ */ jsxs8(SheetPortal, { container: portalContainer, children: [
|
|
1646
|
-
/* @__PURE__ */ jsx26(SheetOverlay, {}),
|
|
1696
|
+
modal ? /* @__PURE__ */ jsx26(SheetOverlay, {}) : null,
|
|
1647
1697
|
/* @__PURE__ */ jsx26(
|
|
1648
1698
|
DialogPrimitive2.Content,
|
|
1649
1699
|
{
|
|
@@ -3883,6 +3933,172 @@ function useActiveShortcuts() {
|
|
|
3883
3933
|
manager.activeBindings
|
|
3884
3934
|
);
|
|
3885
3935
|
}
|
|
3936
|
+
|
|
3937
|
+
// src/components/field-shell.tsx
|
|
3938
|
+
import { cva as cva13 } from "class-variance-authority";
|
|
3939
|
+
import { cloneElement, Fragment as Fragment3, useEffect as useEffect3, useId as useId2 } from "react";
|
|
3940
|
+
import { jsx as jsx40, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
3941
|
+
var controlWidth = cva13("w-full", {
|
|
3942
|
+
variants: {
|
|
3943
|
+
width: {
|
|
3944
|
+
half: "max-w-[var(--nx-field-half,380px)]",
|
|
3945
|
+
full: "max-w-[var(--nx-field-full,760px)]",
|
|
3946
|
+
fill: ""
|
|
3947
|
+
}
|
|
3948
|
+
},
|
|
3949
|
+
defaultVariants: { width: "half" }
|
|
3950
|
+
});
|
|
3951
|
+
function readControlProps(child) {
|
|
3952
|
+
const props = child.props;
|
|
3953
|
+
return props && typeof props === "object" ? props : {};
|
|
3954
|
+
}
|
|
3955
|
+
function isRenderFunction(children) {
|
|
3956
|
+
return typeof children === "function";
|
|
3957
|
+
}
|
|
3958
|
+
function computeControlWiring(ownProps, requestedId, renderedMessageIds, hasError) {
|
|
3959
|
+
const ownId = typeof ownProps.id === "string" && ownProps.id !== "" ? ownProps.id : void 0;
|
|
3960
|
+
const id = ownId ?? requestedId;
|
|
3961
|
+
const ownDescribedByIds = typeof ownProps["aria-describedby"] === "string" ? ownProps["aria-describedby"].split(" ").filter(Boolean) : [];
|
|
3962
|
+
const describedByIds = Array.from(
|
|
3963
|
+
/* @__PURE__ */ new Set([...ownDescribedByIds, ...renderedMessageIds])
|
|
3964
|
+
);
|
|
3965
|
+
const describedBy = describedByIds.length > 0 ? describedByIds.join(" ") : void 0;
|
|
3966
|
+
const ownInvalid = typeof ownProps["aria-invalid"] === "boolean" ? ownProps["aria-invalid"] : void 0;
|
|
3967
|
+
const invalid = hasError ? true : ownInvalid;
|
|
3968
|
+
return { id, describedBy, invalid };
|
|
3969
|
+
}
|
|
3970
|
+
function FieldShell({
|
|
3971
|
+
label,
|
|
3972
|
+
description,
|
|
3973
|
+
error,
|
|
3974
|
+
width = "half",
|
|
3975
|
+
htmlFor,
|
|
3976
|
+
className,
|
|
3977
|
+
children
|
|
3978
|
+
}) {
|
|
3979
|
+
const generatedControlId = useId2();
|
|
3980
|
+
const messageBaseId = useId2();
|
|
3981
|
+
const descriptionId = `${messageBaseId}-description`;
|
|
3982
|
+
const errorId = `${messageBaseId}-error`;
|
|
3983
|
+
const requestedId = htmlFor ?? generatedControlId;
|
|
3984
|
+
const renderedMessageIds = [
|
|
3985
|
+
description ? descriptionId : null,
|
|
3986
|
+
error ? errorId : null
|
|
3987
|
+
].filter((id) => id !== null);
|
|
3988
|
+
const wiring = isRenderFunction(children) ? computeControlWiring({}, requestedId, renderedMessageIds, Boolean(error)) : computeControlWiring(
|
|
3989
|
+
readControlProps(children),
|
|
3990
|
+
requestedId,
|
|
3991
|
+
renderedMessageIds,
|
|
3992
|
+
Boolean(error)
|
|
3993
|
+
);
|
|
3994
|
+
const isFragmentChild = !isRenderFunction(children) && children.type === Fragment3;
|
|
3995
|
+
useEffect3(() => {
|
|
3996
|
+
if (!isDevelopmentRuntime()) return;
|
|
3997
|
+
if (isFragmentChild) return;
|
|
3998
|
+
const landed = document.getElementById(wiring.id) !== null;
|
|
3999
|
+
const fieldName = typeof label === "string" ? `"${label}"` : "this field";
|
|
4000
|
+
devWarnOnce(
|
|
4001
|
+
landed,
|
|
4002
|
+
`FieldShell: the id computed for ${fieldName} never appeared on any element in the document, so its label and any description are not connected to the control. This happens when the control is a compound component \u2014 a Radix \`Select\`, \`RadioGroup\`, or similar \u2014 whose root does not forward \`id\`/\`aria-describedby\`/\`aria-invalid\` to a real DOM node. Pass \`children\` as a function and apply those to the control's actual focusable element (its trigger) instead of a single top-level clone.`
|
|
4003
|
+
);
|
|
4004
|
+
}, [wiring.id, label, isFragmentChild]);
|
|
4005
|
+
let control;
|
|
4006
|
+
if (isRenderFunction(children)) {
|
|
4007
|
+
control = children(wiring);
|
|
4008
|
+
} else {
|
|
4009
|
+
devWarnOnce(
|
|
4010
|
+
!isFragmentChild,
|
|
4011
|
+
"FieldShell: `children` must be a single element, not a Fragment. A Fragment forwards no props to the elements inside it, so the id, aria-describedby and aria-invalid this component computes would have nowhere to land \u2014 the label and the control would look wired up and never actually connect. Wrap the intended elements in a real element instead, such as a `<div>`."
|
|
4012
|
+
);
|
|
4013
|
+
const controlOverrides = {
|
|
4014
|
+
id: wiring.id,
|
|
4015
|
+
...wiring.describedBy !== void 0 ? { "aria-describedby": wiring.describedBy } : {},
|
|
4016
|
+
...wiring.invalid !== void 0 ? { "aria-invalid": wiring.invalid } : {}
|
|
4017
|
+
};
|
|
4018
|
+
control = isFragmentChild ? children : cloneElement(children, controlOverrides);
|
|
4019
|
+
}
|
|
4020
|
+
return /* @__PURE__ */ jsxs18("div", { className: cn("flex flex-col gap-1.5", className), children: [
|
|
4021
|
+
label ? /* @__PURE__ */ jsx40(
|
|
4022
|
+
"label",
|
|
4023
|
+
{
|
|
4024
|
+
htmlFor: wiring.id,
|
|
4025
|
+
className: "text-sm font-medium text-foreground",
|
|
4026
|
+
children: label
|
|
4027
|
+
}
|
|
4028
|
+
) : null,
|
|
4029
|
+
/* @__PURE__ */ jsx40("div", { className: controlWidth({ width }), children: control }),
|
|
4030
|
+
description ? /* @__PURE__ */ jsx40("p", { id: descriptionId, className: "text-sm text-muted-foreground", children: description }) : null,
|
|
4031
|
+
error ? /* @__PURE__ */ jsx40("p", { id: errorId, className: "text-sm font-medium text-destructive", children: error }) : null
|
|
4032
|
+
] });
|
|
4033
|
+
}
|
|
4034
|
+
|
|
4035
|
+
// src/components/form-section.tsx
|
|
4036
|
+
import { useId as useId3 } from "react";
|
|
4037
|
+
import { jsx as jsx41, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
4038
|
+
function FormSection({
|
|
4039
|
+
label,
|
|
4040
|
+
description,
|
|
4041
|
+
className,
|
|
4042
|
+
children
|
|
4043
|
+
}) {
|
|
4044
|
+
const labelId = useId3();
|
|
4045
|
+
return /* @__PURE__ */ jsxs19("section", { "aria-labelledby": labelId, className: cn("space-y-2", className), children: [
|
|
4046
|
+
/* @__PURE__ */ jsxs19("div", { className: "space-y-1", children: [
|
|
4047
|
+
/* @__PURE__ */ jsx41(
|
|
4048
|
+
"p",
|
|
4049
|
+
{
|
|
4050
|
+
id: labelId,
|
|
4051
|
+
className: "text-xs font-semibold tracking-[0.08em] uppercase text-muted-foreground",
|
|
4052
|
+
children: label
|
|
4053
|
+
}
|
|
4054
|
+
),
|
|
4055
|
+
description ? /* @__PURE__ */ jsx41("p", { className: "text-sm text-muted-foreground", children: description }) : null
|
|
4056
|
+
] }),
|
|
4057
|
+
/* @__PURE__ */ jsx41(Card, { className: "overflow-hidden", children: /* @__PURE__ */ jsx41("div", { className: "divide-y divide-foreground/10 px-6", children }) })
|
|
4058
|
+
] });
|
|
4059
|
+
}
|
|
4060
|
+
|
|
4061
|
+
// src/components/form-layout.tsx
|
|
4062
|
+
import { cva as cva14 } from "class-variance-authority";
|
|
4063
|
+
import { jsx as jsx42, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
4064
|
+
var measure = cva14("mx-auto w-full px-6 py-8", {
|
|
4065
|
+
variants: {
|
|
4066
|
+
width: {
|
|
4067
|
+
form: "max-w-[56rem]",
|
|
4068
|
+
wide: "max-w-[72rem]"
|
|
4069
|
+
}
|
|
4070
|
+
},
|
|
4071
|
+
defaultVariants: { width: "form" }
|
|
4072
|
+
});
|
|
4073
|
+
function FormLayout({
|
|
4074
|
+
width = "form",
|
|
4075
|
+
className,
|
|
4076
|
+
children
|
|
4077
|
+
}) {
|
|
4078
|
+
return /* @__PURE__ */ jsx42("div", { className: cn(measure({ width }), className), children });
|
|
4079
|
+
}
|
|
4080
|
+
function FormActions({ dirty, className, children }) {
|
|
4081
|
+
return /* @__PURE__ */ jsxs20(
|
|
4082
|
+
"div",
|
|
4083
|
+
{
|
|
4084
|
+
className: cn(
|
|
4085
|
+
"sticky bottom-0 mt-8 flex items-center justify-end gap-3",
|
|
4086
|
+
"border-t border-border bg-background/95 py-4 backdrop-blur",
|
|
4087
|
+
className
|
|
4088
|
+
),
|
|
4089
|
+
children: [
|
|
4090
|
+
dirty ? (
|
|
4091
|
+
// `role="status"` (implying `aria-live="polite"`) so a screen-reader
|
|
4092
|
+
// user is told when the flag flips false to true, not only sighted
|
|
4093
|
+
// users watching the bar. This is the only live region FormActions
|
|
4094
|
+
// renders, one per form.
|
|
4095
|
+
/* @__PURE__ */ jsx42("span", { role: "status", className: "mr-auto text-sm text-muted-foreground", children: "You have unsaved changes" })
|
|
4096
|
+
) : null,
|
|
4097
|
+
children
|
|
4098
|
+
]
|
|
4099
|
+
}
|
|
4100
|
+
);
|
|
4101
|
+
}
|
|
3886
4102
|
export {
|
|
3887
4103
|
Accordion,
|
|
3888
4104
|
AccordionContent,
|
|
@@ -3967,7 +4183,11 @@ export {
|
|
|
3967
4183
|
DropdownMenuSubContent,
|
|
3968
4184
|
DropdownMenuSubTrigger,
|
|
3969
4185
|
DropdownMenuTrigger,
|
|
4186
|
+
FieldShell,
|
|
4187
|
+
FormActions,
|
|
3970
4188
|
FormLabelWithTooltip,
|
|
4189
|
+
FormLayout,
|
|
4190
|
+
FormSection,
|
|
3971
4191
|
Grid,
|
|
3972
4192
|
Input,
|
|
3973
4193
|
Label,
|