@lattice-php/lattice 0.16.0 → 0.17.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.
- package/dist/action/components/action-form.js +1 -1
- package/dist/action/use-action.js +1 -1
- package/dist/chat/types.d.ts +2 -10
- package/dist/core/components/button.js +34 -18
- package/dist/core/components/button.js.map +1 -1
- package/dist/core/components/control.d.ts +0 -2
- package/dist/core/components/control.js.map +1 -1
- package/dist/core/components/link.js +25 -29
- package/dist/core/components/link.js.map +1 -1
- package/dist/core/renderer.js +42 -2
- package/dist/core/renderer.js.map +1 -1
- package/dist/core/types.d.ts +22 -42
- package/dist/core/use-click-behavior.d.ts +42 -0
- package/dist/core/use-click-behavior.js +46 -0
- package/dist/core/use-click-behavior.js.map +1 -0
- package/dist/create-app.d.ts +8 -2
- package/dist/create-app.js +4 -2
- package/dist/create-app.js.map +1 -1
- package/dist/effects/registry.d.ts +9 -20
- package/dist/effects/registry.js +4 -6
- package/dist/effects/registry.js.map +1 -1
- package/dist/events/event-bridge.d.ts +1 -1
- package/dist/events/event-bridge.js +1 -1
- package/dist/events/event-bridge.js.map +1 -1
- package/dist/form/components/base/input-otp.d.ts +2 -1
- package/dist/form/components/base/input-otp.js.map +1 -1
- package/dist/form/components/conditions.d.ts +1 -1
- package/dist/form/components/conditions.js.map +1 -1
- package/dist/form/components/field-props.d.ts +2 -1
- package/dist/form/components/field-props.js.map +1 -1
- package/dist/form/components/field-scope.d.ts +2 -1
- package/dist/form/components/field-scope.js.map +1 -1
- package/dist/form/components/fields/row-action-menu.d.ts +2 -1
- package/dist/form/components/fields/row-action-menu.js.map +1 -1
- package/dist/form/components/fields/row-item.d.ts +2 -1
- package/dist/form/components/fields/row-item.js.map +1 -1
- package/dist/form/components/fields/table-rows.d.ts +2 -1
- package/dist/form/components/fields/table-rows.js.map +1 -1
- package/dist/form/components/fields/time-picker-columns.d.ts +2 -1
- package/dist/form/components/fields/time-picker-columns.js.map +1 -1
- package/dist/form/components/fields/time-picker.d.ts +2 -1
- package/dist/form/components/fields/time-picker.js.map +1 -1
- package/dist/form/components/fields/use-row-collection.d.ts +2 -1
- package/dist/form/components/fields/use-row-collection.js.map +1 -1
- package/dist/form/components/form-path.d.ts +0 -1
- package/dist/form/components/form-path.js +1 -1
- package/dist/form/components/form-path.js.map +1 -1
- package/dist/form/components/prefill-targets.d.ts +1 -1
- package/dist/form/components/prefill-targets.js.map +1 -1
- package/dist/form/components/types.d.ts +5 -1
- package/dist/form/components/use-field-commit.d.ts +2 -1
- package/dist/form/components/use-field-commit.js.map +1 -1
- package/dist/form/components/use-form-resolver.d.ts +2 -1
- package/dist/form/components/use-form-resolver.js.map +1 -1
- package/dist/format/date-time.d.ts +12 -0
- package/dist/format/date-time.js +30 -0
- package/dist/format/date-time.js.map +1 -0
- package/dist/format/value.js +1 -1
- package/dist/format/value.js.map +1 -1
- package/dist/i18n/config.d.ts +2 -1
- package/dist/i18n/config.js.map +1 -1
- package/dist/i18n/date-time.js +1 -1
- package/dist/i18n/date-time.js.map +1 -1
- package/dist/i18n/instance.d.ts +1 -1
- package/dist/i18n/instance.js.map +1 -1
- package/dist/i18n/locale-reload.d.ts +2 -1
- package/dist/i18n/locale-reload.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/layout/components.js +1 -1
- package/dist/layout/menu-item.js +43 -49
- package/dist/layout/menu-item.js.map +1 -1
- package/dist/notifications/api.d.ts +6 -0
- package/dist/notifications/api.js +21 -0
- package/dist/notifications/api.js.map +1 -0
- package/dist/notifications/components/empty-state.d.ts +1 -0
- package/dist/notifications/components/empty-state.js +21 -0
- package/dist/notifications/components/empty-state.js.map +1 -0
- package/dist/notifications/components/notification-item.d.ts +6 -0
- package/dist/notifications/components/notification-item.js +65 -0
- package/dist/notifications/components/notification-item.js.map +1 -0
- package/dist/notifications/components/notification-list.d.ts +10 -0
- package/dist/notifications/components/notification-list.js +38 -0
- package/dist/notifications/components/notification-list.js.map +1 -0
- package/dist/notifications/components/notifications-echo.d.ts +5 -0
- package/dist/notifications/components/notifications-echo.js +22 -0
- package/dist/notifications/components/notifications-echo.js.map +1 -0
- package/dist/notifications/components/notifications.d.ts +3 -0
- package/dist/notifications/components/notifications.js +109 -0
- package/dist/notifications/components/notifications.js.map +1 -0
- package/dist/notifications/index.d.ts +1 -0
- package/dist/notifications/index.js +11 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/notifications/store.d.ts +25 -0
- package/dist/notifications/store.js +100 -0
- package/dist/notifications/store.js.map +1 -0
- package/dist/notifications/types.d.ts +10 -0
- package/dist/notifications/types.js +0 -0
- package/dist/realtime/build-effects.d.ts +2 -1
- package/dist/realtime/build-effects.js.map +1 -1
- package/dist/realtime/subscriptions.js.map +1 -1
- package/dist/registry/eager.js +2 -1
- package/dist/registry/eager.js.map +1 -1
- package/dist/registry/lazy.js +2 -1
- package/dist/registry/lazy.js.map +1 -1
- package/dist/table/bulk.d.ts +2 -1
- package/dist/table/bulk.js.map +1 -1
- package/dist/table/components/bulk-bar.js +2 -2
- package/dist/table/components/cells/money-cell.js +1 -1
- package/dist/table/components/cells/number-cell.js +1 -1
- package/dist/table/components/cells/text-cell.js +1 -1
- package/dist/table/components/filter-controls.d.ts +0 -4
- package/dist/table/components/filter-controls.js.map +1 -1
- package/dist/table/components/table-cell.js.map +1 -1
- package/dist/table/format.d.ts +1 -12
- package/dist/table/format.js +2 -26
- package/dist/table/format.js.map +1 -1
- package/dist/table/types.d.ts +7 -17
- package/dist/types/generated.d.ts +119 -290
- package/dist/vite-client.d.ts +8 -0
- package/dist/vite.d.ts +35 -1
- package/dist/vite.js +60 -2
- package/dist/vite.js.map +1 -1
- package/package.json +17 -2
- package/resources/icons/bell.svg +16 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Renderer } from "../../core/renderer.js";
|
|
2
2
|
import { useT } from "../../i18n/instance.js";
|
|
3
|
-
import { dispatchActionError } from "../../effects/dispatch.js";
|
|
4
3
|
import { Button } from "../../core/components/button.js";
|
|
5
4
|
import { Dialog, DialogContent, DialogHeader } from "../../core/components/dialog.js";
|
|
6
5
|
import { Spinner } from "../../core/components/spinner.js";
|
|
6
|
+
import { dispatchActionError } from "../../effects/dispatch.js";
|
|
7
7
|
import { apiFetch } from "../../core/api.js";
|
|
8
8
|
import { Skeleton } from "../../core/components/skeleton.js";
|
|
9
9
|
import { FormProvider } from "../../form/components/context.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { withHeaders } from "../core/headers.js";
|
|
2
|
+
import { ConfirmDialog } from "../core/components/confirm-dialog.js";
|
|
2
3
|
import { getActionEffects } from "../effects/dispatch.js";
|
|
3
4
|
import { useEffectDispatcher } from "../effects/use-effect-dispatcher.js";
|
|
4
|
-
import { ConfirmDialog } from "../core/components/confirm-dialog.js";
|
|
5
5
|
import { runAction } from "./run-action.js";
|
|
6
6
|
import { ActionForm, useLazyActionForm } from "./components/action-form.js";
|
|
7
7
|
import { router, useHttp } from "@inertiajs/react";
|
package/dist/chat/types.d.ts
CHANGED
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
import { Node } from '../core/types';
|
|
2
|
-
import { ChatMessage
|
|
3
|
-
export type { ChatRole };
|
|
4
|
-
/**
|
|
5
|
-
* The generated message shape, but `parts` is widened from the generated
|
|
6
|
-
* `ChatNode` union to open `Node`s — a part is just a component node rendered by
|
|
7
|
-
* `type`, so a message can also carry a consumer's custom part.
|
|
8
|
-
*/
|
|
9
|
-
export type ChatMessage = Omit<GeneratedChatMessage, "parts"> & {
|
|
10
|
-
parts: Node[];
|
|
11
|
-
};
|
|
2
|
+
import { ChatMessage, ChatRole } from '../types/generated';
|
|
3
|
+
export type { ChatMessage, ChatRole };
|
|
12
4
|
export type ChatStatus = "idle" | "submitted" | "streaming" | "error";
|
|
13
5
|
export type ChatFrame = {
|
|
14
6
|
type: "text";
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { cn } from "../../lib/utils.js";
|
|
2
2
|
import { IconRenderer } from "../../icons/icon-renderer.js";
|
|
3
3
|
import { nodeIdentity } from "../test-id.js";
|
|
4
|
-
import {
|
|
5
|
-
import { useEffectDispatcher } from "../../effects/use-effect-dispatcher.js";
|
|
4
|
+
import { ActionTrigger, useClickBehavior } from "../use-click-behavior.js";
|
|
6
5
|
import { Link } from "@inertiajs/react";
|
|
7
6
|
import "react";
|
|
8
7
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -45,34 +44,51 @@ function Button({ className, variant, size, asChild = false, ...props }) {
|
|
|
45
44
|
});
|
|
46
45
|
}
|
|
47
46
|
var ButtonComponent = ({ node }) => {
|
|
48
|
-
const {
|
|
49
|
-
const effects = node.props.effects ?? [];
|
|
47
|
+
const { label, icon } = node.props;
|
|
50
48
|
const variant = node.props.variant ?? "default";
|
|
51
49
|
const testId = nodeIdentity(node);
|
|
52
|
-
const
|
|
53
|
-
|
|
50
|
+
const behavior = useClickBehavior(node.props);
|
|
51
|
+
const size = icon ? "icon" : "default";
|
|
52
|
+
const content = icon ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(IconRenderer, {
|
|
53
|
+
className: "size-lt-icon-md",
|
|
54
|
+
icon
|
|
55
|
+
}), label ? /* @__PURE__ */ jsx("span", {
|
|
56
|
+
className: "sr-only",
|
|
57
|
+
children: label
|
|
58
|
+
}) : null] }) : label;
|
|
59
|
+
const triggerButton = ({ onClick, processing }) => /* @__PURE__ */ jsx(Button, {
|
|
60
|
+
"data-test": testId,
|
|
61
|
+
disabled: processing,
|
|
62
|
+
onClick,
|
|
63
|
+
size,
|
|
64
|
+
variant,
|
|
65
|
+
children: content
|
|
66
|
+
});
|
|
67
|
+
if (behavior.kind === "navigate") return /* @__PURE__ */ jsx(Button, {
|
|
54
68
|
asChild: true,
|
|
55
69
|
"data-test": testId,
|
|
56
70
|
variant,
|
|
57
|
-
size
|
|
71
|
+
size,
|
|
58
72
|
children: /* @__PURE__ */ jsx(Link, {
|
|
59
|
-
href,
|
|
60
|
-
|
|
73
|
+
href: behavior.href,
|
|
74
|
+
method: behavior.method,
|
|
75
|
+
children: content
|
|
61
76
|
})
|
|
62
77
|
});
|
|
78
|
+
if (behavior.kind === "action") return /* @__PURE__ */ jsx(ActionTrigger, {
|
|
79
|
+
action: behavior.action,
|
|
80
|
+
children: triggerButton
|
|
81
|
+
});
|
|
82
|
+
if (behavior.kind === "effects") return triggerButton({
|
|
83
|
+
onClick: behavior.onClick,
|
|
84
|
+
processing: false
|
|
85
|
+
});
|
|
63
86
|
return /* @__PURE__ */ jsx(Button, {
|
|
64
87
|
"data-test": testId,
|
|
65
|
-
|
|
66
|
-
size: icon ? "icon" : "default",
|
|
88
|
+
size,
|
|
67
89
|
type: node.props.buttonType,
|
|
68
90
|
variant,
|
|
69
|
-
children:
|
|
70
|
-
className: "size-lt-icon-md",
|
|
71
|
-
icon
|
|
72
|
-
}), label ? /* @__PURE__ */ jsx("span", {
|
|
73
|
-
className: "sr-only",
|
|
74
|
-
children: label
|
|
75
|
-
}) : null] }) : label
|
|
91
|
+
children: content
|
|
76
92
|
});
|
|
77
93
|
};
|
|
78
94
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.js","names":[],"sources":["../../../resources/js/core/components/button.tsx"],"sourcesContent":["import { Link } from \"@inertiajs/react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport {
|
|
1
|
+
{"version":3,"file":"button.js","names":[],"sources":["../../../resources/js/core/components/button.tsx"],"sourcesContent":["import { Link } from \"@inertiajs/react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { IconRenderer } from \"@lattice-php/lattice/icons\";\nimport { nodeIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport {\n ActionTrigger,\n type TriggerState,\n useClickBehavior,\n} from \"@lattice-php/lattice/core/use-click-behavior\";\nimport type { ButtonVariant } from \"@lattice-php/lattice/types/generated\";\n\nexport type { ButtonVariant };\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lt-sm text-base font-medium transition-[color,box-shadow] disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-lt-icon-md [&_svg]:shrink-0 outline-none focus-visible:border-lt-ring focus-visible:ring-lt-ring/50 focus-visible:ring-[3px] aria-invalid:ring-lt-danger/20 dark:aria-invalid:ring-lt-danger/40 aria-invalid:border-lt-danger\",\n {\n variants: {\n variant: {\n default:\n \"bg-lt-primary text-lt-primary-fg shadow-lt-xs hover:bg-lt-primary-hover active:bg-lt-primary-active disabled:bg-lt-disabled disabled:text-lt-disabled-fg disabled:shadow-none\",\n destructive:\n \"bg-lt-danger text-lt-danger-fg shadow-lt-xs hover:bg-lt-danger-hover active:bg-lt-danger-active focus-visible:ring-lt-danger/20 dark:focus-visible:ring-lt-danger/40 disabled:bg-lt-disabled disabled:text-lt-disabled-fg disabled:shadow-none\",\n success:\n \"bg-lt-success text-lt-success-fg shadow-lt-xs hover:bg-lt-success-hover active:bg-lt-success-active disabled:bg-lt-disabled disabled:text-lt-disabled-fg disabled:shadow-none\",\n info: \"bg-lt-info text-lt-info-fg shadow-lt-xs hover:bg-lt-info-hover active:bg-lt-info-active disabled:bg-lt-disabled disabled:text-lt-disabled-fg disabled:shadow-none\",\n outline:\n \"border border-lt-input bg-lt-bg shadow-lt-xs hover:bg-lt-accent hover:text-lt-accent-fg disabled:bg-lt-disabled disabled:text-lt-disabled-fg disabled:border-transparent disabled:shadow-none\",\n secondary:\n \"bg-lt-secondary text-lt-secondary-fg shadow-lt-xs hover:bg-lt-secondary-hover active:bg-lt-secondary-active disabled:bg-lt-disabled disabled:text-lt-disabled-fg disabled:shadow-none\",\n ghost: \"hover:bg-lt-accent hover:text-lt-accent-fg disabled:text-lt-disabled-fg\",\n link: \"text-lt-primary underline-offset-4 hover:underline disabled:text-lt-disabled-fg disabled:no-underline\",\n },\n size: {\n default: \"h-lt-control-md px-4 py-2 has-[>svg]:px-3\",\n sm: \"h-lt-control-sm rounded-lt-sm px-3 has-[>svg]:px-2.5\",\n lg: \"h-lt-control-lg rounded-lt-sm px-6 has-[>svg]:px-4\",\n icon: \"size-lt-control-md\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nfunction Button({\n className,\n variant,\n size,\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean;\n }) {\n const Comp = asChild ? Slot : \"button\";\n\n return (\n <Comp\n data-slot=\"button\"\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n );\n}\n\nconst ButtonComponent: RendererComponent<\"button\"> = ({ node }) => {\n const { label, icon } = node.props;\n const variant = node.props.variant ?? \"default\";\n const testId = nodeIdentity(node);\n const behavior = useClickBehavior(node.props);\n const size = icon ? \"icon\" : \"default\";\n const content = icon ? (\n <>\n <IconRenderer className=\"size-lt-icon-md\" icon={icon} />\n {label ? <span className=\"sr-only\">{label}</span> : null}\n </>\n ) : (\n label\n );\n\n const triggerButton = ({ onClick, processing }: TriggerState) => (\n <Button\n data-test={testId}\n disabled={processing}\n onClick={onClick}\n size={size}\n variant={variant}\n >\n {content}\n </Button>\n );\n\n if (behavior.kind === \"navigate\") {\n return (\n <Button asChild data-test={testId} variant={variant} size={size}>\n <Link href={behavior.href} method={behavior.method}>\n {content}\n </Link>\n </Button>\n );\n }\n\n if (behavior.kind === \"action\") {\n return <ActionTrigger action={behavior.action}>{triggerButton}</ActionTrigger>;\n }\n\n if (behavior.kind === \"effects\") {\n return triggerButton({ onClick: behavior.onClick, processing: false });\n }\n\n return (\n <Button data-test={testId} size={size} type={node.props.buttonType} variant={variant}>\n {content}\n </Button>\n );\n};\n\nexport default ButtonComponent;\nexport { Button, buttonVariants };\n"],"mappings":";;;;;;;;;;AAiBA,IAAM,iBAAiB,IACrB,+bACA;CACE,UAAU;EACR,SAAS;GACP,SACE;GACF,aACE;GACF,SACE;GACF,MAAM;GACN,SACE;GACF,WACE;GACF,OAAO;GACP,MAAM;EACR;EACA,MAAM;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,MAAM;EACR;CACF;CACA,iBAAiB;EACf,SAAS;EACT,MAAM;CACR;AACF,CACF;AAEA,SAAS,OAAO,EACd,WACA,SACA,MACA,UAAU,OACV,GAAG,SAIA;CAGH,OACE,oBAHW,UAAU,OAAO,UAG5B;EACE,aAAU;EACV,WAAW,GAAG,eAAe;GAAE;GAAS;GAAM;EAAU,CAAC,CAAC;EAC1D,GAAI;CACL,CAAA;AAEL;AAEA,IAAM,mBAAgD,EAAE,WAAW;CACjE,MAAM,EAAE,OAAO,SAAS,KAAK;CAC7B,MAAM,UAAU,KAAK,MAAM,WAAW;CACtC,MAAM,SAAS,aAAa,IAAI;CAChC,MAAM,WAAW,iBAAiB,KAAK,KAAK;CAC5C,MAAM,OAAO,OAAO,SAAS;CAC7B,MAAM,UAAU,OACd,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,cAAD;EAAc,WAAU;EAAwB;CAAO,CAAA,GACtD,QAAQ,oBAAC,QAAD;EAAM,WAAU;YAAW;CAAY,CAAA,IAAI,IACpD,EAAA,CAAA,IAEF;CAGF,MAAM,iBAAiB,EAAE,SAAS,iBAChC,oBAAC,QAAD;EACE,aAAW;EACX,UAAU;EACD;EACH;EACG;YAER;CACK,CAAA;CAGV,IAAI,SAAS,SAAS,YACpB,OACE,oBAAC,QAAD;EAAQ,SAAA;EAAQ,aAAW;EAAiB;EAAe;YACzD,oBAAC,MAAD;GAAM,MAAM,SAAS;GAAM,QAAQ,SAAS;aACzC;EACG,CAAA;CACA,CAAA;CAIZ,IAAI,SAAS,SAAS,UACpB,OAAO,oBAAC,eAAD;EAAe,QAAQ,SAAS;YAAS;CAA6B,CAAA;CAG/E,IAAI,SAAS,SAAS,WACpB,OAAO,cAAc;EAAE,SAAS,SAAS;EAAS,YAAY;CAAM,CAAC;CAGvE,OACE,oBAAC,QAAD;EAAQ,aAAW;EAAc;EAAM,MAAM,KAAK,MAAM;EAAqB;YAC1E;CACK,CAAA;AAEZ"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { VariantProps } from 'class-variance-authority';
|
|
2
1
|
export declare const FOCUS_RING = "focus-visible:border-lt-ring focus-visible:ring-lt-ring/50 focus-visible:ring-[3px]";
|
|
3
2
|
/**
|
|
4
3
|
* The shared chrome for single-line form/table controls (text inputs, native
|
|
@@ -9,4 +8,3 @@ export declare const FOCUS_RING = "focus-visible:border-lt-ring focus-visible:ri
|
|
|
9
8
|
export declare const controlSurface: (props?: ({
|
|
10
9
|
density?: "compact" | "comfortable" | null | undefined;
|
|
11
10
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
12
|
-
export type ControlSurfaceVariants = VariantProps<typeof controlSurface>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control.js","names":[],"sources":["../../../resources/js/core/components/control.ts"],"sourcesContent":["import { cva
|
|
1
|
+
{"version":3,"file":"control.js","names":[],"sources":["../../../resources/js/core/components/control.ts"],"sourcesContent":["import { cva } from \"class-variance-authority\";\n\nexport const FOCUS_RING =\n \"focus-visible:border-lt-ring focus-visible:ring-lt-ring/50 focus-visible:ring-[3px]\";\n\n/**\n * The shared chrome for single-line form/table controls (text inputs, native\n * selects, and the Select/filter trigger buttons). `density` is the only axis\n * that differs between forms (comfortable) and table filters (compact); the\n * border, focus ring, invalid, and disabled treatment are unified.\n */\nexport const controlSurface = cva(\n [\n \"w-full min-w-0 rounded-lt-sm border border-lt-input shadow-lt-xs outline-none transition-[color,box-shadow]\",\n \"placeholder:text-lt-muted-fg\",\n FOCUS_RING,\n \"aria-invalid:border-lt-danger aria-invalid:ring-lt-danger/20 dark:aria-invalid:ring-lt-danger/40\",\n \"disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-lt-disabled disabled:text-lt-disabled-fg\",\n ],\n {\n variants: {\n density: {\n comfortable: \"h-lt-control-md bg-transparent px-3 py-1 text-base\",\n compact: \"h-lt-control-md bg-lt-bg px-2 text-sm font-normal\",\n },\n },\n defaultVariants: { density: \"comfortable\" },\n },\n);\n"],"mappings":";;AAEA,IAAa,aACX;;;;;;;AAQF,IAAa,iBAAiB,IAC5B;CACE;CACA;CACA;CACA;CACA;AACF,GACA;CACE,UAAU,EACR,SAAS;EACP,aAAa;EACb,SAAS;CACX,EACF;CACA,iBAAiB,EAAE,SAAS,cAAc;AAC5C,CACF"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cn } from "../../lib/utils.js";
|
|
2
2
|
import { IconRenderer } from "../../icons/icon-renderer.js";
|
|
3
3
|
import { nodeIdentity } from "../test-id.js";
|
|
4
|
-
import {
|
|
4
|
+
import { ActionTrigger, useClickBehavior } from "../use-click-behavior.js";
|
|
5
5
|
import { actionMenuItemClassName, useActionMenu } from "../../action/components/action-menu-context.js";
|
|
6
6
|
import { Link } from "@inertiajs/react";
|
|
7
7
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -14,18 +14,6 @@ function TextLink({ className = "", children, ...props }) {
|
|
|
14
14
|
children
|
|
15
15
|
});
|
|
16
16
|
}
|
|
17
|
-
function ActionLink({ action, ariaLabel, className, children, testId }) {
|
|
18
|
-
const { processing, requestSubmit, overlays } = useAction(action);
|
|
19
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("button", {
|
|
20
|
-
"aria-label": ariaLabel,
|
|
21
|
-
className: cn(textLinkClassName, className),
|
|
22
|
-
"data-test": testId,
|
|
23
|
-
disabled: processing,
|
|
24
|
-
onClick: requestSubmit,
|
|
25
|
-
type: "button",
|
|
26
|
-
children
|
|
27
|
-
}), overlays] });
|
|
28
|
-
}
|
|
29
17
|
function LinkAffix({ affix, className }) {
|
|
30
18
|
if (affix.icon) return /* @__PURE__ */ jsx(IconRenderer, {
|
|
31
19
|
className: cn("size-lt-icon-md shrink-0", className),
|
|
@@ -69,12 +57,10 @@ function labelWithTextAffixes(label, prefix, suffix) {
|
|
|
69
57
|
}
|
|
70
58
|
var LinkComponent = ({ node }) => {
|
|
71
59
|
const isMenuItem = useActionMenu();
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
const suffix = node.props.suffix;
|
|
77
|
-
const iconOnly = Boolean(icon);
|
|
60
|
+
const { icon, label, prefix, suffix } = node.props;
|
|
61
|
+
const behavior = useClickBehavior(node.props);
|
|
62
|
+
const className = isMenuItem ? actionMenuItemClassName : void 0;
|
|
63
|
+
const testId = nodeIdentity(node);
|
|
78
64
|
const content = /* @__PURE__ */ jsx(LinkContent, {
|
|
79
65
|
icon,
|
|
80
66
|
isMenuItem,
|
|
@@ -82,20 +68,30 @@ var LinkComponent = ({ node }) => {
|
|
|
82
68
|
prefix,
|
|
83
69
|
suffix
|
|
84
70
|
});
|
|
85
|
-
const ariaLabel =
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
71
|
+
const ariaLabel = icon ? label : labelWithTextAffixes(label, prefix, suffix);
|
|
72
|
+
const triggerButton = ({ onClick, processing }) => /* @__PURE__ */ jsx("button", {
|
|
73
|
+
"aria-label": ariaLabel,
|
|
74
|
+
className: cn(textLinkClassName, className),
|
|
75
|
+
"data-test": testId,
|
|
76
|
+
disabled: processing,
|
|
77
|
+
onClick,
|
|
78
|
+
type: "button",
|
|
91
79
|
children: content
|
|
92
80
|
});
|
|
81
|
+
if (behavior.kind === "action") return /* @__PURE__ */ jsx(ActionTrigger, {
|
|
82
|
+
action: behavior.action,
|
|
83
|
+
children: triggerButton
|
|
84
|
+
});
|
|
85
|
+
if (behavior.kind === "effects") return triggerButton({
|
|
86
|
+
onClick: behavior.onClick,
|
|
87
|
+
processing: false
|
|
88
|
+
});
|
|
93
89
|
return /* @__PURE__ */ jsx(TextLink, {
|
|
94
90
|
"aria-label": ariaLabel,
|
|
95
|
-
className
|
|
96
|
-
"data-test":
|
|
97
|
-
href:
|
|
98
|
-
method:
|
|
91
|
+
className,
|
|
92
|
+
"data-test": testId,
|
|
93
|
+
href: behavior.kind === "navigate" ? behavior.href : "#",
|
|
94
|
+
method: behavior.kind === "navigate" ? behavior.method : "get",
|
|
99
95
|
tabIndex: node.props.tabIndex ?? void 0,
|
|
100
96
|
children: content
|
|
101
97
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"link.js","names":[],"sources":["../../../resources/js/core/components/link.tsx"],"sourcesContent":["import { Link } from \"@inertiajs/react\";\nimport type { ComponentProps
|
|
1
|
+
{"version":3,"file":"link.js","names":[],"sources":["../../../resources/js/core/components/link.tsx"],"sourcesContent":["import { Link } from \"@inertiajs/react\";\nimport type { ComponentProps } from \"react\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { nodeIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport {\n ActionTrigger,\n type TriggerState,\n useClickBehavior,\n} from \"@lattice-php/lattice/core/use-click-behavior\";\nimport { IconRenderer } from \"@lattice-php/lattice/icons\";\nimport type { Affix } from \"@lattice-php/lattice/types/generated\";\nimport {\n actionMenuItemClassName,\n useActionMenu,\n} from \"@lattice-php/lattice/action/components/action-menu-context\";\n\nconst textLinkClassName =\n \"text-lt-fg underline decoration-lt-border underline-offset-4 transition-colors duration-300 ease-out hover:decoration-current! dark:decoration-lt-border\";\n\nfunction TextLink({ className = \"\", children, ...props }: ComponentProps<typeof Link>) {\n return (\n <Link className={cn(textLinkClassName, className)} {...props}>\n {children}\n </Link>\n );\n}\n\nfunction LinkAffix({ affix, className }: { affix: Affix; className?: string }) {\n if (affix.icon) {\n return <IconRenderer className={cn(\"size-lt-icon-md shrink-0\", className)} icon={affix.icon} />;\n }\n\n return <span className={cn(\"shrink-0 text-sm text-lt-muted-fg\", className)}>{affix.text}</span>;\n}\n\nfunction LinkContent({\n icon,\n isMenuItem,\n label,\n prefix,\n suffix,\n}: {\n icon?: string | null;\n isMenuItem: boolean;\n label: string;\n prefix?: Affix | null;\n suffix?: Affix | null;\n}) {\n if (icon) {\n return <IconRenderer className=\"size-lt-icon-md shrink-0\" icon={icon} />;\n }\n\n if (isMenuItem) {\n return (\n <>\n {prefix ? <LinkAffix affix={prefix} /> : null}\n <span>{label}</span>\n {suffix ? <LinkAffix affix={suffix} className=\"ml-auto\" /> : null}\n </>\n );\n }\n\n if (prefix || suffix) {\n return (\n <span className=\"inline-flex items-center gap-1.5 align-baseline\">\n {prefix ? <LinkAffix affix={prefix} /> : null}\n <span>{label}</span>\n {suffix ? <LinkAffix affix={suffix} /> : null}\n </span>\n );\n }\n\n return label;\n}\n\nfunction labelWithTextAffixes(label: string, prefix?: Affix | null, suffix?: Affix | null) {\n if (!prefix?.text && !suffix?.text) {\n return undefined;\n }\n\n return [prefix?.text, label, suffix?.text]\n .filter((part): part is string => Boolean(part))\n .join(\" \");\n}\n\nconst LinkComponent: RendererComponent<\"link\"> = ({ node }) => {\n const isMenuItem = useActionMenu();\n const { icon, label, prefix, suffix } = node.props;\n const behavior = useClickBehavior(node.props);\n const className = isMenuItem ? actionMenuItemClassName : undefined;\n const testId = nodeIdentity(node);\n const content = (\n <LinkContent\n icon={icon}\n isMenuItem={isMenuItem}\n label={label}\n prefix={prefix}\n suffix={suffix}\n />\n );\n const ariaLabel = icon ? label : labelWithTextAffixes(label, prefix, suffix);\n\n const triggerButton = ({ onClick, processing }: TriggerState) => (\n <button\n aria-label={ariaLabel}\n className={cn(textLinkClassName, className)}\n data-test={testId}\n disabled={processing}\n onClick={onClick}\n type=\"button\"\n >\n {content}\n </button>\n );\n\n if (behavior.kind === \"action\") {\n return <ActionTrigger action={behavior.action}>{triggerButton}</ActionTrigger>;\n }\n\n if (behavior.kind === \"effects\") {\n return triggerButton({ onClick: behavior.onClick, processing: false });\n }\n\n return (\n <TextLink\n aria-label={ariaLabel}\n className={className}\n data-test={testId}\n href={behavior.kind === \"navigate\" ? behavior.href : \"#\"}\n method={behavior.kind === \"navigate\" ? behavior.method : \"get\"}\n tabIndex={node.props.tabIndex ?? undefined}\n >\n {content}\n </TextLink>\n );\n};\n\nexport default LinkComponent;\nexport { TextLink };\n"],"mappings":";;;;;;;;AAiBA,IAAM,oBACJ;AAEF,SAAS,SAAS,EAAE,YAAY,IAAI,UAAU,GAAG,SAAsC;CACrF,OACE,oBAAC,MAAD;EAAM,WAAW,GAAG,mBAAmB,SAAS;EAAG,GAAI;EACpD;CACG,CAAA;AAEV;AAEA,SAAS,UAAU,EAAE,OAAO,aAAmD;CAC7E,IAAI,MAAM,MACR,OAAO,oBAAC,cAAD;EAAc,WAAW,GAAG,4BAA4B,SAAS;EAAG,MAAM,MAAM;CAAO,CAAA;CAGhG,OAAO,oBAAC,QAAD;EAAM,WAAW,GAAG,qCAAqC,SAAS;YAAI,MAAM;CAAW,CAAA;AAChG;AAEA,SAAS,YAAY,EACnB,MACA,YACA,OACA,QACA,UAOC;CACD,IAAI,MACF,OAAO,oBAAC,cAAD;EAAc,WAAU;EAAiC;CAAO,CAAA;CAGzE,IAAI,YACF,OACE,qBAAA,UAAA,EAAA,UAAA;EACG,SAAS,oBAAC,WAAD,EAAW,OAAO,OAAS,CAAA,IAAI;EACzC,oBAAC,QAAD,EAAA,UAAO,MAAY,CAAA;EAClB,SAAS,oBAAC,WAAD;GAAW,OAAO;GAAQ,WAAU;EAAW,CAAA,IAAI;CAC7D,EAAA,CAAA;CAIN,IAAI,UAAU,QACZ,OACE,qBAAC,QAAD;EAAM,WAAU;YAAhB;GACG,SAAS,oBAAC,WAAD,EAAW,OAAO,OAAS,CAAA,IAAI;GACzC,oBAAC,QAAD,EAAA,UAAO,MAAY,CAAA;GAClB,SAAS,oBAAC,WAAD,EAAW,OAAO,OAAS,CAAA,IAAI;EACrC;;CAIV,OAAO;AACT;AAEA,SAAS,qBAAqB,OAAe,QAAuB,QAAuB;CACzF,IAAI,CAAC,QAAQ,QAAQ,CAAC,QAAQ,MAC5B;CAGF,OAAO;EAAC,QAAQ;EAAM;EAAO,QAAQ;CAAI,EACtC,QAAQ,SAAyB,QAAQ,IAAI,CAAC,EAC9C,KAAK,GAAG;AACb;AAEA,IAAM,iBAA4C,EAAE,WAAW;CAC7D,MAAM,aAAa,cAAc;CACjC,MAAM,EAAE,MAAM,OAAO,QAAQ,WAAW,KAAK;CAC7C,MAAM,WAAW,iBAAiB,KAAK,KAAK;CAC5C,MAAM,YAAY,aAAa,0BAA0B,KAAA;CACzD,MAAM,SAAS,aAAa,IAAI;CAChC,MAAM,UACJ,oBAAC,aAAD;EACQ;EACM;EACL;EACC;EACA;CACT,CAAA;CAEH,MAAM,YAAY,OAAO,QAAQ,qBAAqB,OAAO,QAAQ,MAAM;CAE3E,MAAM,iBAAiB,EAAE,SAAS,iBAChC,oBAAC,UAAD;EACE,cAAY;EACZ,WAAW,GAAG,mBAAmB,SAAS;EAC1C,aAAW;EACX,UAAU;EACD;EACT,MAAK;YAEJ;CACK,CAAA;CAGV,IAAI,SAAS,SAAS,UACpB,OAAO,oBAAC,eAAD;EAAe,QAAQ,SAAS;YAAS;CAA6B,CAAA;CAG/E,IAAI,SAAS,SAAS,WACpB,OAAO,cAAc;EAAE,SAAS,SAAS;EAAS,YAAY;CAAM,CAAC;CAGvE,OACE,oBAAC,UAAD;EACE,cAAY;EACD;EACX,aAAW;EACX,MAAM,SAAS,SAAS,aAAa,SAAS,OAAO;EACrD,QAAQ,SAAS,SAAS,aAAa,SAAS,SAAS;EACzD,UAAU,KAAK,MAAM,YAAY,KAAA;YAEhC;CACO,CAAA;AAEd"}
|
package/dist/core/renderer.js
CHANGED
|
@@ -1,10 +1,50 @@
|
|
|
1
1
|
import { useComponentRegistry } from "./registry-context.js";
|
|
2
2
|
import { useCollapsed } from "./collapsed-context.js";
|
|
3
3
|
import { Suspense } from "react";
|
|
4
|
-
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region resources/js/core/renderer.tsx
|
|
6
|
+
function MissingComponentIcon() {
|
|
7
|
+
return /* @__PURE__ */ jsxs("svg", {
|
|
8
|
+
"aria-hidden": "true",
|
|
9
|
+
className: "size-lt-icon-md shrink-0",
|
|
10
|
+
fill: "none",
|
|
11
|
+
stroke: "currentColor",
|
|
12
|
+
strokeLinecap: "round",
|
|
13
|
+
strokeLinejoin: "round",
|
|
14
|
+
strokeWidth: "2",
|
|
15
|
+
viewBox: "0 0 24 24",
|
|
16
|
+
children: [
|
|
17
|
+
/* @__PURE__ */ jsx("rect", {
|
|
18
|
+
height: "18",
|
|
19
|
+
rx: "2",
|
|
20
|
+
strokeDasharray: "4 3",
|
|
21
|
+
width: "18",
|
|
22
|
+
x: "3",
|
|
23
|
+
y: "3"
|
|
24
|
+
}),
|
|
25
|
+
/* @__PURE__ */ jsx("path", { d: "M12 8v4" }),
|
|
26
|
+
/* @__PURE__ */ jsx("path", { d: "M12 16h.01" })
|
|
27
|
+
]
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Fallback for a node whose type has no registered renderer. Always renders a
|
|
32
|
+
* visible, muted marker — icon-only survives tight spots like table cells — so
|
|
33
|
+
* the gap is never invisible. Shows the type inline in development; keeps it
|
|
34
|
+
* screen-reader-only (plus a hover tooltip) in production.
|
|
35
|
+
*/
|
|
6
36
|
function MissingComponent({ node }) {
|
|
7
|
-
|
|
37
|
+
node.type;
|
|
38
|
+
const label = `Missing component: ${node.type}`;
|
|
39
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
40
|
+
className: "inline-flex items-center gap-1.5 align-middle text-lt-muted-fg",
|
|
41
|
+
"data-lattice-missing-component": node.type,
|
|
42
|
+
title: label,
|
|
43
|
+
children: [/* @__PURE__ */ jsx(MissingComponentIcon, {}), /* @__PURE__ */ jsx("span", {
|
|
44
|
+
className: "sr-only",
|
|
45
|
+
children: label
|
|
46
|
+
})]
|
|
47
|
+
});
|
|
8
48
|
}
|
|
9
49
|
function nodeKey(node, index) {
|
|
10
50
|
return node.key ?? node.id ?? `${node.type}-${index}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderer.js","names":[],"sources":["../../resources/js/core/renderer.tsx"],"sourcesContent":["import { Suspense } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { Node } from \"./types\";\nimport { useCollapsed } from \"./collapsed-context\";\nimport { useComponentRegistry } from \"./registry-context\";\n\
|
|
1
|
+
{"version":3,"file":"renderer.js","names":[],"sources":["../../resources/js/core/renderer.tsx"],"sourcesContent":["import { Suspense } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { Node } from \"./types\";\nimport { useCollapsed } from \"./collapsed-context\";\nimport { useComponentRegistry } from \"./registry-context\";\n\nconst warnedMissingTypes = new Set<string>();\n\nfunction warnMissingComponent(type: string): void {\n if (!import.meta.env.DEV || warnedMissingTypes.has(type)) {\n return;\n }\n\n warnedMissingTypes.add(type);\n console.warn(\n `[lattice] No component registered for node type \"${type}\" — Lattice rendered a fallback ` +\n \"placeholder. Likely causes: your app registry was not passed to \" +\n \"createLatticeApp({ registry }), or the registry key does not match the PHP \" +\n \"#[AsComponent]/AsField type.\",\n );\n}\n\nfunction MissingComponentIcon() {\n return (\n <svg\n aria-hidden=\"true\"\n className=\"size-lt-icon-md shrink-0\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth=\"2\"\n viewBox=\"0 0 24 24\"\n >\n <rect height=\"18\" rx=\"2\" strokeDasharray=\"4 3\" width=\"18\" x=\"3\" y=\"3\" />\n <path d=\"M12 8v4\" />\n <path d=\"M12 16h.01\" />\n </svg>\n );\n}\n\n/**\n * Fallback for a node whose type has no registered renderer. Always renders a\n * visible, muted marker — icon-only survives tight spots like table cells — so\n * the gap is never invisible. Shows the type inline in development; keeps it\n * screen-reader-only (plus a hover tooltip) in production.\n */\nfunction MissingComponent({ node }: { node: Node }) {\n warnMissingComponent(node.type);\n\n const label = `Missing component: ${node.type}`;\n\n return (\n <span\n className=\"inline-flex items-center gap-1.5 align-middle text-lt-muted-fg\"\n data-lattice-missing-component={node.type}\n title={label}\n >\n <MissingComponentIcon />\n <span className={import.meta.env.DEV ? \"text-sm\" : \"sr-only\"}>{label}</span>\n </span>\n );\n}\n\nfunction nodeKey(node: Node, index: number): string {\n return node.key ?? node.id ?? `${node.type}-${index}`;\n}\n\nexport function Renderer({ nodes }: { nodes: Node[] }): ReactNode {\n return nodes.map((node, index) => <NodeRenderer key={nodeKey(node, index)} node={node} />);\n}\n\nexport function RenderNode({ node }: { node: Node }): ReactNode {\n return <NodeRenderer node={node} />;\n}\n\nfunction NodeRenderer({ node }: { node: Node }) {\n const collapsed = useCollapsed();\n const registry = useComponentRegistry();\n const registration = registry[node.type];\n\n if (collapsed && node.props?.hideWhenCollapsed === true) {\n return null;\n }\n\n if (!registration) {\n return <MissingComponent node={node} />;\n }\n\n const Component = registration.component;\n const children = node.schema?.length ? <Renderer nodes={node.schema} /> : null;\n const renderedComponent = <Component node={node}>{children}</Component>;\n\n if (registration.mode === \"lazy\") {\n const FallbackComponent = registration.fallback;\n const fallback = FallbackComponent ? (\n <FallbackComponent node={node}>{null}</FallbackComponent>\n ) : null;\n\n return <Suspense fallback={fallback}>{renderedComponent}</Suspense>;\n }\n\n return renderedComponent;\n}\n"],"mappings":";;;;;AAsBA,SAAS,uBAAuB;CAC9B,OACE,qBAAC,OAAD;EACE,eAAY;EACZ,WAAU;EACV,MAAK;EACL,QAAO;EACP,eAAc;EACd,gBAAe;EACf,aAAY;EACZ,SAAQ;YARV;GAUE,oBAAC,QAAD;IAAM,QAAO;IAAK,IAAG;IAAI,iBAAgB;IAAM,OAAM;IAAK,GAAE;IAAI,GAAE;GAAK,CAAA;GACvE,oBAAC,QAAD,EAAM,GAAE,UAAW,CAAA;GACnB,oBAAC,QAAD,EAAM,GAAE,aAAc,CAAA;EACnB;;AAET;;;;;;;AAQA,SAAS,iBAAiB,EAAE,QAAwB;CAClD,KAA0B;CAE1B,MAAM,QAAQ,sBAAsB,KAAK;CAEzC,OACE,qBAAC,QAAD;EACE,WAAU;EACV,kCAAgC,KAAK;EACrC,OAAO;YAHT,CAKE,oBAAC,sBAAD,CAAuB,CAAA,GACvB,oBAAC,QAAD;GAAM,WAA6C;aAAY;EAAY,CAAA,CACvE;;AAEV;AAEA,SAAS,QAAQ,MAAY,OAAuB;CAClD,OAAO,KAAK,OAAO,KAAK,MAAM,GAAG,KAAK,KAAK,GAAG;AAChD;AAEA,SAAgB,SAAS,EAAE,SAAuC;CAChE,OAAO,MAAM,KAAK,MAAM,UAAU,oBAAC,cAAD,EAA+C,KAAO,GAAnC,QAAQ,MAAM,KAAK,CAAgB,CAAC;AAC3F;AAEA,SAAgB,WAAW,EAAE,QAAmC;CAC9D,OAAO,oBAAC,cAAD,EAAoB,KAAO,CAAA;AACpC;AAEA,SAAS,aAAa,EAAE,QAAwB;CAC9C,MAAM,YAAY,aAAa;CAE/B,MAAM,eADW,qBACI,EAAS,KAAK;CAEnC,IAAI,aAAa,KAAK,OAAO,sBAAsB,MACjD,OAAO;CAGT,IAAI,CAAC,cACH,OAAO,oBAAC,kBAAD,EAAwB,KAAO,CAAA;CAGxC,MAAM,YAAY,aAAa;CAE/B,MAAM,oBAAoB,oBAAC,WAAD;EAAiB;EAAO,UADjC,KAAK,QAAQ,SAAS,oBAAC,UAAD,EAAU,OAAO,KAAK,OAAS,CAAA,IAAI;CACJ,CAAA;CAEtE,IAAI,aAAa,SAAS,QAAQ;EAChC,MAAM,oBAAoB,aAAa;EAKvC,OAAO,oBAAC,UAAD;GAAoB,UAJV,oBACf,oBAAC,mBAAD;IAAyB;cAAO;GAAwB,CAAA,IACtD;aAEkC;EAA4B,CAAA;CACpE;CAEA,OAAO;AACT"}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -1,58 +1,33 @@
|
|
|
1
1
|
import { ComponentType as ReactComponentType, ReactNode } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
export type { KnownPageContainer, NodeType, WireNode };
|
|
4
|
-
/** Loose props bag for nodes whose `type` is not a generated built-in. */
|
|
2
|
+
import { ComponentPropsMap, NodeType, Option, PageContainer as KnownPageContainer, WireNode, ListenerPayload } from '../types/generated';
|
|
3
|
+
export type { KnownPageContainer, NodeType, Option, WireNode };
|
|
5
4
|
export type NodeProps = Record<string, unknown>;
|
|
6
5
|
export type CommonNodeProps = {
|
|
7
6
|
dataBindings?: Record<string, string> | null;
|
|
8
7
|
hideWhenCollapsed?: boolean | null;
|
|
9
8
|
};
|
|
10
|
-
/** A `{ label, value }` pair used by the choice, select and segmented controls. */
|
|
11
|
-
export type { Option };
|
|
12
9
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
props?: NodeProps;
|
|
20
|
-
schema?: Schema;
|
|
21
|
-
type: TType;
|
|
22
|
-
};
|
|
23
|
-
/**
|
|
24
|
-
* Augmentable map of wire `type` string → props. Consumer apps extend it via
|
|
25
|
-
* `declare module "@lattice-php/lattice"` so their custom components gain strong
|
|
26
|
-
* prop types; the entries are generated by `php artisan lattice:typescript`.
|
|
27
|
-
* Built-in components resolve through the generated `WireNode` union below, so
|
|
28
|
-
* this interface starts empty. Types listed in neither fall back to a loose bag.
|
|
10
|
+
* Augmentable map of wire `type` string → props for consumer components. Apps
|
|
11
|
+
* extend it via `declare module "@lattice-php/lattice"` so their custom components
|
|
12
|
+
* gain strong prop types; the entries are generated by `php artisan lattice:typescript`.
|
|
13
|
+
* Built-in components resolve through the generated `ComponentPropsMap` below, the
|
|
14
|
+
* same way `ColumnProps` augments `ColumnPropsMap`. Types in neither fall back to a
|
|
15
|
+
* loose bag.
|
|
29
16
|
*/
|
|
30
17
|
export interface ComponentProps {
|
|
31
18
|
}
|
|
32
19
|
/**
|
|
33
|
-
* Resolves a
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* every extensible kind resolves the same way.
|
|
20
|
+
* Resolves a `type` string to its payload: consumer augmentations (`TAugment`)
|
|
21
|
+
* win over generated built-ins (`TBuiltins`), then a loose fallback. The one
|
|
22
|
+
* resolver behind the component, column and effect maps.
|
|
37
23
|
*/
|
|
38
24
|
export type ResolveProps<TAugment, TBuiltins, TType extends string, TFallback> = TType extends keyof TAugment ? TAugment[TType] : TType extends keyof TBuiltins ? TBuiltins[TType] : TFallback;
|
|
39
|
-
type
|
|
40
|
-
[TNode in WireNode as TNode["type"]]: TNode extends {
|
|
41
|
-
props: infer TProps;
|
|
42
|
-
} ? TProps : never;
|
|
43
|
-
};
|
|
44
|
-
/**
|
|
45
|
-
* Resolves a wire `type` to its props: consumer augmentations
|
|
46
|
-
* (`ComponentProps`) first, then generated built-ins, then a loose bag.
|
|
47
|
-
*/
|
|
48
|
-
export type PropsOf<TType extends string> = ResolveProps<ComponentProps, WireNodeProps, TType, NodeProps> & CommonNodeProps;
|
|
25
|
+
export type PropsOf<TType extends string> = ResolveProps<ComponentProps, ComponentPropsMap, TType, NodeProps> & CommonNodeProps;
|
|
49
26
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* components still type-check. Built-in `props` is always present — the wire
|
|
53
|
-
* serializes the full prop object, so reads need no optional chaining.
|
|
27
|
+
* Built-in `props` is required, not optional — the wire serializes the full prop
|
|
28
|
+
* object, so reads need no optional chaining. Unknown types fall back to `WireNode`.
|
|
54
29
|
*/
|
|
55
|
-
export type NodeOfType<TType extends string = string> = string extends TType ?
|
|
30
|
+
export type NodeOfType<TType extends string = string> = string extends TType ? WireNode : {
|
|
56
31
|
id?: string;
|
|
57
32
|
key?: string;
|
|
58
33
|
props: PropsOf<TType>;
|
|
@@ -60,9 +35,14 @@ export type NodeOfType<TType extends string = string> = string extends TType ? L
|
|
|
60
35
|
type: TType;
|
|
61
36
|
};
|
|
62
37
|
export type Node<TType extends string = string> = NodeOfType<TType>;
|
|
63
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* Distributes so `NodeUnionOf<"a" | "b">` is `NodeOfType<"a"> | NodeOfType<"b">`
|
|
40
|
+
* — a discriminated union where `node.type` narrows `node.props`. Domains build
|
|
41
|
+
* their node union from a generated `…NodeType` string union with this.
|
|
42
|
+
*/
|
|
43
|
+
export type NodeUnionOf<TTypes extends string> = TTypes extends string ? NodeOfType<TTypes> : never;
|
|
64
44
|
export type Schema = Node[];
|
|
65
|
-
/**
|
|
45
|
+
/** Its `schema` holds exactly one Outlet node. */
|
|
66
46
|
export type LayoutPayload = {
|
|
67
47
|
key: string;
|
|
68
48
|
schema: Schema;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Method } from '@inertiajs/core';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { Node } from './types';
|
|
4
|
+
import { Effect } from '../types/generated';
|
|
5
|
+
export type ClickBehavior = {
|
|
6
|
+
kind: "navigate";
|
|
7
|
+
href: string;
|
|
8
|
+
method: Method;
|
|
9
|
+
} | {
|
|
10
|
+
kind: "action";
|
|
11
|
+
action: Node<"action">;
|
|
12
|
+
} | {
|
|
13
|
+
kind: "effects";
|
|
14
|
+
onClick: () => void;
|
|
15
|
+
} | {
|
|
16
|
+
kind: "none";
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* The shared click model for Link/Button/MenuItem: a clickable carries exactly
|
|
20
|
+
* one behavior — navigate to an href, run a server action, or dispatch client
|
|
21
|
+
* effects. This resolves it without touching the action machinery, so plain
|
|
22
|
+
* links/labels stay cheap; the `action` behavior renders through {@link ActionTrigger}.
|
|
23
|
+
*/
|
|
24
|
+
export declare function useClickBehavior(props: {
|
|
25
|
+
href?: string | null;
|
|
26
|
+
method?: Method | null;
|
|
27
|
+
action?: Node | null;
|
|
28
|
+
effects?: Effect[] | null;
|
|
29
|
+
}): ClickBehavior;
|
|
30
|
+
export type TriggerState = {
|
|
31
|
+
onClick: () => void;
|
|
32
|
+
processing: boolean;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Runs a server action's gate (confirm/form) and renders its overlays, handing
|
|
36
|
+
* the host a click handler + processing state to render its own element with.
|
|
37
|
+
* Only mounted for the `action` behavior, so a plain link never runs `useAction`.
|
|
38
|
+
*/
|
|
39
|
+
export declare function ActionTrigger({ action, children, }: {
|
|
40
|
+
action: Node<"action">;
|
|
41
|
+
children: (trigger: TriggerState) => ReactNode;
|
|
42
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { getActionEffects } from "../effects/dispatch.js";
|
|
2
|
+
import { useEffectDispatcher } from "../effects/use-effect-dispatcher.js";
|
|
3
|
+
import { useAction } from "../action/use-action.js";
|
|
4
|
+
import { Fragment, jsxs } from "react/jsx-runtime";
|
|
5
|
+
//#region resources/js/core/use-click-behavior.tsx
|
|
6
|
+
/**
|
|
7
|
+
* The shared click model for Link/Button/MenuItem: a clickable carries exactly
|
|
8
|
+
* one behavior — navigate to an href, run a server action, or dispatch client
|
|
9
|
+
* effects. This resolves it without touching the action machinery, so plain
|
|
10
|
+
* links/labels stay cheap; the `action` behavior renders through {@link ActionTrigger}.
|
|
11
|
+
*/
|
|
12
|
+
function useClickBehavior(props) {
|
|
13
|
+
const dispatch = useEffectDispatcher();
|
|
14
|
+
const action = props.action ?? null;
|
|
15
|
+
const effects = props.effects ?? [];
|
|
16
|
+
if (action) return {
|
|
17
|
+
kind: "action",
|
|
18
|
+
action
|
|
19
|
+
};
|
|
20
|
+
if (effects.length > 0) return {
|
|
21
|
+
kind: "effects",
|
|
22
|
+
onClick: () => dispatch(getActionEffects(effects))
|
|
23
|
+
};
|
|
24
|
+
if (props.href != null && props.href !== "") return {
|
|
25
|
+
kind: "navigate",
|
|
26
|
+
href: props.href,
|
|
27
|
+
method: props.method ?? "get"
|
|
28
|
+
};
|
|
29
|
+
return { kind: "none" };
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Runs a server action's gate (confirm/form) and renders its overlays, handing
|
|
33
|
+
* the host a click handler + processing state to render its own element with.
|
|
34
|
+
* Only mounted for the `action` behavior, so a plain link never runs `useAction`.
|
|
35
|
+
*/
|
|
36
|
+
function ActionTrigger({ action, children }) {
|
|
37
|
+
const { processing, requestSubmit, overlays } = useAction(action);
|
|
38
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [children({
|
|
39
|
+
onClick: requestSubmit,
|
|
40
|
+
processing
|
|
41
|
+
}), overlays] });
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
export { ActionTrigger, useClickBehavior };
|
|
45
|
+
|
|
46
|
+
//# sourceMappingURL=use-click-behavior.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-click-behavior.js","names":[],"sources":["../../resources/js/core/use-click-behavior.tsx"],"sourcesContent":["import type { Method } from \"@inertiajs/core\";\nimport type { ReactNode } from \"react\";\nimport { useAction } from \"@lattice-php/lattice/action/use-action\";\nimport type { Node } from \"@lattice-php/lattice/core/types\";\nimport { getActionEffects } from \"@lattice-php/lattice/effects/dispatch\";\nimport { useEffectDispatcher } from \"@lattice-php/lattice/effects/use-effect-dispatcher\";\nimport type { Effect } from \"@lattice-php/lattice/types/generated\";\n\nexport type ClickBehavior =\n | { kind: \"navigate\"; href: string; method: Method }\n | { kind: \"action\"; action: Node<\"action\"> }\n | { kind: \"effects\"; onClick: () => void }\n | { kind: \"none\" };\n\n/**\n * The shared click model for Link/Button/MenuItem: a clickable carries exactly\n * one behavior — navigate to an href, run a server action, or dispatch client\n * effects. This resolves it without touching the action machinery, so plain\n * links/labels stay cheap; the `action` behavior renders through {@link ActionTrigger}.\n */\nexport function useClickBehavior(props: {\n href?: string | null;\n method?: Method | null;\n action?: Node | null;\n effects?: Effect[] | null;\n}): ClickBehavior {\n const dispatch = useEffectDispatcher();\n const action = (props.action ?? null) as Node<\"action\"> | null;\n const effects = props.effects ?? [];\n\n if (action) {\n return { kind: \"action\", action };\n }\n\n if (effects.length > 0) {\n return { kind: \"effects\", onClick: () => dispatch(getActionEffects(effects)) };\n }\n\n if (props.href != null && props.href !== \"\") {\n return { kind: \"navigate\", href: props.href, method: props.method ?? \"get\" };\n }\n\n return { kind: \"none\" };\n}\n\nexport type TriggerState = { onClick: () => void; processing: boolean };\n\n/**\n * Runs a server action's gate (confirm/form) and renders its overlays, handing\n * the host a click handler + processing state to render its own element with.\n * Only mounted for the `action` behavior, so a plain link never runs `useAction`.\n */\nexport function ActionTrigger({\n action,\n children,\n}: {\n action: Node<\"action\">;\n children: (trigger: TriggerState) => ReactNode;\n}) {\n const { processing, requestSubmit, overlays } = useAction(action);\n\n return (\n <>\n {children({ onClick: requestSubmit, processing })}\n {overlays}\n </>\n );\n}\n"],"mappings":";;;;;;;;;;;AAoBA,SAAgB,iBAAiB,OAKf;CAChB,MAAM,WAAW,oBAAoB;CACrC,MAAM,SAAU,MAAM,UAAU;CAChC,MAAM,UAAU,MAAM,WAAW,CAAC;CAElC,IAAI,QACF,OAAO;EAAE,MAAM;EAAU;CAAO;CAGlC,IAAI,QAAQ,SAAS,GACnB,OAAO;EAAE,MAAM;EAAW,eAAe,SAAS,iBAAiB,OAAO,CAAC;CAAE;CAG/E,IAAI,MAAM,QAAQ,QAAQ,MAAM,SAAS,IACvC,OAAO;EAAE,MAAM;EAAY,MAAM,MAAM;EAAM,QAAQ,MAAM,UAAU;CAAM;CAG7E,OAAO,EAAE,MAAM,OAAO;AACxB;;;;;;AASA,SAAgB,cAAc,EAC5B,QACA,YAIC;CACD,MAAM,EAAE,YAAY,eAAe,aAAa,UAAU,MAAM;CAEhE,OACE,qBAAA,UAAA,EAAA,UAAA,CACG,SAAS;EAAE,SAAS;EAAe;CAAW,CAAC,GAC/C,QACD,EAAA,CAAA;AAEN"}
|
package/dist/create-app.d.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { createInertiaApp } from '@inertiajs/react';
|
|
2
2
|
import { ReactElement } from 'react';
|
|
3
|
-
import { Registry } from './core/registry';
|
|
3
|
+
import { Plugin, Registry } from './core/registry';
|
|
4
4
|
import { SpriteValue } from './icons/sprite';
|
|
5
5
|
import { CreateLayoutResolverOptions, PageModules } from './inertia';
|
|
6
6
|
type InertiaAppOptions = NonNullable<Parameters<typeof createInertiaApp>[0]>;
|
|
7
7
|
export type CreateLatticeAppOptions = Omit<InertiaAppOptions, "resolve" | "layout" | "setup" | "withApp" | "pages"> & {
|
|
8
8
|
registry?: Registry;
|
|
9
|
+
/**
|
|
10
|
+
* Component-package plugins to merge onto the registry — pass the
|
|
11
|
+
* Composer-discovered `virtual:lattice/plugins` here to register vendor
|
|
12
|
+
* components with no manual `extendRegistry` call.
|
|
13
|
+
*/
|
|
14
|
+
plugins?: Plugin[];
|
|
9
15
|
sprite?: SpriteValue;
|
|
10
16
|
/** Normal Inertia page modules, e.g. `import.meta.glob('./pages/**\/*.tsx')`. */
|
|
11
17
|
pages?: PageModules;
|
|
@@ -17,5 +23,5 @@ export type CreateLatticeAppOptions = Omit<InertiaAppOptions, "resolve" | "layou
|
|
|
17
23
|
* registry, sprite, flash toasts via Lattice's own Toaster — and theme
|
|
18
24
|
* initialization. Forwards any other createInertiaApp option.
|
|
19
25
|
*/
|
|
20
|
-
export declare function createLatticeApp({ registry, sprite, pages, defaultLayout, strictMode, ...inertiaOptions }?: CreateLatticeAppOptions): Promise<void | ((page: import('@inertiajs/core').Page<import('@inertiajs/core').PageProps>, renderToString: (element: ReactElement) => string) => Promise<import('@inertiajs/core').InertiaAppSSRResponse>)>;
|
|
26
|
+
export declare function createLatticeApp({ registry, plugins, sprite, pages, defaultLayout, strictMode, ...inertiaOptions }?: CreateLatticeAppOptions): Promise<void | ((page: import('@inertiajs/core').Page<import('@inertiajs/core').PageProps>, renderToString: (element: ReactElement) => string) => Promise<import('@inertiajs/core').InertiaAppSSRResponse>)>;
|
|
21
27
|
export {};
|
package/dist/create-app.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { initializeTheme } from "./appearance/index.js";
|
|
2
|
+
import { extendRegistry } from "./core/registry.js";
|
|
2
3
|
import { setDefaultRegistry } from "./core/registry-context.js";
|
|
3
4
|
import { createLayoutResolver, createPageResolver } from "./inertia.js";
|
|
4
5
|
import { ProviderBase } from "./provider-base.js";
|
|
@@ -12,8 +13,9 @@ import { jsx } from "react/jsx-runtime";
|
|
|
12
13
|
* registry, sprite, flash toasts via Lattice's own Toaster — and theme
|
|
13
14
|
* initialization. Forwards any other createInertiaApp option.
|
|
14
15
|
*/
|
|
15
|
-
function createLatticeApp({ registry, sprite, pages = {}, defaultLayout, strictMode = true, ...inertiaOptions } = {}) {
|
|
16
|
-
const
|
|
16
|
+
function createLatticeApp({ registry, plugins, sprite, pages = {}, defaultLayout, strictMode = true, ...inertiaOptions } = {}) {
|
|
17
|
+
const baseRegistry = registry ?? lazyRegistry;
|
|
18
|
+
const activeRegistry = plugins && plugins.length > 0 ? extendRegistry(baseRegistry, ...plugins) : baseRegistry;
|
|
17
19
|
setDefaultRegistry(activeRegistry);
|
|
18
20
|
const app = createInertiaApp({
|
|
19
21
|
...inertiaOptions,
|