@lattice-php/lattice 0.14.0 → 0.16.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/action/components/action-group.js +1 -1
- package/dist/action/components/action-group.js.map +1 -1
- package/dist/action/components/action-menu-context.d.ts +1 -1
- package/dist/action/components/action-menu-context.js +1 -1
- package/dist/action/components/action-menu-context.js.map +1 -1
- package/dist/core/components/card.js +4 -4
- package/dist/core/components/card.js.map +1 -1
- package/dist/core/components/chart.js +49 -2
- package/dist/core/components/chart.js.map +1 -1
- package/dist/core/components/collapsible.js +1 -0
- package/dist/core/components/collapsible.js.map +1 -1
- package/dist/core/components/control.d.ts +1 -1
- package/dist/core/components/dialog.d.ts +1 -1
- package/dist/core/components/dialog.js +2 -2
- package/dist/core/components/dialog.js.map +1 -1
- package/dist/core/components/floating-panel.js +2 -2
- package/dist/core/components/floating-panel.js.map +1 -1
- package/dist/core/components/grid.js +1 -0
- package/dist/core/components/grid.js.map +1 -1
- package/dist/core/components/link.js +61 -3
- package/dist/core/components/link.js.map +1 -1
- package/dist/core/components/popover.d.ts +1 -1
- package/dist/core/components/popover.js +1 -1
- package/dist/core/components/popover.js.map +1 -1
- package/dist/core/components/section.js +4 -3
- package/dist/core/components/section.js.map +1 -1
- package/dist/core/components/stack.js +1 -0
- package/dist/core/components/stack.js.map +1 -1
- package/dist/form/components/base/submit-button.js +1 -1
- package/dist/form/components/base/submit-button.js.map +1 -1
- package/dist/form/components/fields/date-picker-control.js +13 -15
- package/dist/form/components/fields/date-picker-control.js.map +1 -1
- package/dist/form/components/fields/rich-editor.js +1 -1
- package/dist/form/components/fields/rich-editor.js.map +1 -1
- package/dist/form/components/fields/time-input.js +54 -15
- package/dist/form/components/fields/time-input.js.map +1 -1
- package/dist/form/components/fields/time-picker-columns.d.ts +23 -0
- package/dist/form/components/fields/time-picker-columns.js +76 -0
- package/dist/form/components/fields/time-picker-columns.js.map +1 -0
- package/dist/form/components/fields/time-picker.d.ts +17 -0
- package/dist/form/components/fields/time-picker.js +132 -0
- package/dist/form/components/fields/time-picker.js.map +1 -0
- package/dist/form/components/form.js +2 -1
- package/dist/form/components/form.js.map +1 -1
- package/dist/format/number.d.ts +2 -0
- package/dist/format/number.js +23 -0
- package/dist/format/number.js.map +1 -0
- package/dist/{table/components/cells → format}/numeric.js +1 -1
- package/dist/format/numeric.js.map +1 -0
- package/dist/format/value.d.ts +6 -0
- package/dist/format/value.js +17 -0
- package/dist/format/value.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/lattice.css +68 -36
- package/dist/layout/menu-item.js +1 -1
- package/dist/layout/menu-item.js.map +1 -1
- package/dist/layout/sidebar.js +2 -2
- package/dist/layout/sidebar.js.map +1 -1
- package/dist/layout/topbar.js +1 -1
- package/dist/layout/topbar.js.map +1 -1
- package/dist/table/components/cells/money-cell.js +11 -12
- package/dist/table/components/cells/money-cell.js.map +1 -1
- package/dist/table/components/cells/number-cell.js +11 -11
- package/dist/table/components/cells/number-cell.js.map +1 -1
- package/dist/table/components/column-filter-control.js +2 -2
- package/dist/table/components/column-filter-control.js.map +1 -1
- package/dist/table/components/filter-controls.js +2 -2
- package/dist/table/components/filter-controls.js.map +1 -1
- package/dist/table/components/pagination.js +7 -6
- package/dist/table/components/pagination.js.map +1 -1
- package/dist/table/components/table.js +9 -3
- package/dist/table/components/table.js.map +1 -1
- package/dist/table/format.d.ts +2 -0
- package/dist/table/format.js +2 -0
- package/dist/table/format.js.map +1 -1
- package/dist/toast/toaster.js +1 -1
- package/dist/toast/toaster.js.map +1 -1
- package/dist/types/generated.d.ts +21 -0
- package/dist/types/index.d.ts +1 -1
- package/package.json +1 -1
- package/resources/icons/clock.svg +16 -0
- package/dist/table/components/cells/numeric.js.map +0 -1
- /package/dist/{table/components/cells → format}/numeric.d.ts +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sidebar.js","names":[],"sources":["../../resources/js/layout/sidebar.tsx"],"sourcesContent":["import { router } from \"@inertiajs/react\";\nimport { useEffect, useState } from \"react\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { CollapsedContext } from \"../core/collapsed-context\";\nimport { LATTICE_EVENT } from \"@lattice-php/lattice/events/event-names\";\nimport { nodeIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\n\nconst DESKTOP_QUERY = \"(min-width: 768px)\";\n\nfunction readStored(key: string, remember: boolean): boolean {\n if (!remember || typeof window === \"undefined\") {\n return false;\n }\n\n return window.localStorage.getItem(key) === \"true\";\n}\n\nfunction matchesTarget(event: Event, identity: string | undefined): boolean {\n const target = (event as CustomEvent<{ target?: string }>).detail?.target;\n\n return target == null || target === identity;\n}\n\nfunction useIsDesktop(): boolean {\n const supported = () => typeof window !== \"undefined\" && typeof window.matchMedia === \"function\";\n const [isDesktop, setIsDesktop] = useState(() =>\n supported() ? window.matchMedia(DESKTOP_QUERY).matches : true,\n );\n\n useEffect(() => {\n if (!supported()) {\n return;\n }\n\n const query = window.matchMedia(DESKTOP_QUERY);\n const update = (): void => setIsDesktop(query.matches);\n\n update();\n query.addEventListener(\"change\", update);\n\n return () => query.removeEventListener(\"change\", update);\n }, []);\n\n return isDesktop;\n}\n\nconst SidebarComponent: RendererComponent<\"sidebar\"> = ({ children, node }) => {\n const collapsible = node.props.collapsible;\n const rememberState = node.props.rememberState;\n const identity = nodeIdentity(node);\n const storageKey = `lattice:sidebar:${identity ?? \"default\"}`;\n const isDesktop = useIsDesktop();\n\n const [collapsed, setCollapsed] = useState(() =>\n readStored(storageKey, collapsible && rememberState),\n );\n const [mobileOpen, setMobileOpen] = useState(false);\n\n useEffect(() => {\n function toggle(event: Event): void {\n if (!matchesTarget(event, identity)) {\n return;\n }\n\n if (window.matchMedia?.(DESKTOP_QUERY).matches ?? true) {\n if (!collapsible) {\n return;\n }\n\n setCollapsed((value) => {\n const next = !value;\n\n if (rememberState && typeof window !== \"undefined\") {\n window.localStorage.setItem(storageKey, String(next));\n }\n\n return next;\n });\n } else {\n setMobileOpen((open) => !open);\n }\n }\n\n window.addEventListener(LATTICE_EVENT.toggleSidebar, toggle);\n\n return () => window.removeEventListener(LATTICE_EVENT.toggleSidebar, toggle);\n }, [identity, collapsible, rememberState, storageKey]);\n\n useEffect(() => router.on(\"navigate\", () => setMobileOpen(false)), []);\n\n useEffect(() => {\n if (!mobileOpen) {\n return;\n }\n\n function close(event: KeyboardEvent): void {\n if (event.key === \"Escape\") {\n setMobileOpen(false);\n }\n }\n\n window.addEventListener(\"keydown\", close);\n\n return () => window.removeEventListener(\"keydown\", close);\n }, [mobileOpen]);\n\n const isCollapsed = collapsible && collapsed && isDesktop;\n\n return (\n <CollapsedContext.Provider value={isCollapsed}>\n {mobileOpen ? (\n <div\n aria-hidden=\"true\"\n className=\"fixed inset-0 z-
|
|
1
|
+
{"version":3,"file":"sidebar.js","names":[],"sources":["../../resources/js/layout/sidebar.tsx"],"sourcesContent":["import { router } from \"@inertiajs/react\";\nimport { useEffect, useState } from \"react\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { CollapsedContext } from \"../core/collapsed-context\";\nimport { LATTICE_EVENT } from \"@lattice-php/lattice/events/event-names\";\nimport { nodeIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\n\nconst DESKTOP_QUERY = \"(min-width: 768px)\";\n\nfunction readStored(key: string, remember: boolean): boolean {\n if (!remember || typeof window === \"undefined\") {\n return false;\n }\n\n return window.localStorage.getItem(key) === \"true\";\n}\n\nfunction matchesTarget(event: Event, identity: string | undefined): boolean {\n const target = (event as CustomEvent<{ target?: string }>).detail?.target;\n\n return target == null || target === identity;\n}\n\nfunction useIsDesktop(): boolean {\n const supported = () => typeof window !== \"undefined\" && typeof window.matchMedia === \"function\";\n const [isDesktop, setIsDesktop] = useState(() =>\n supported() ? window.matchMedia(DESKTOP_QUERY).matches : true,\n );\n\n useEffect(() => {\n if (!supported()) {\n return;\n }\n\n const query = window.matchMedia(DESKTOP_QUERY);\n const update = (): void => setIsDesktop(query.matches);\n\n update();\n query.addEventListener(\"change\", update);\n\n return () => query.removeEventListener(\"change\", update);\n }, []);\n\n return isDesktop;\n}\n\nconst SidebarComponent: RendererComponent<\"sidebar\"> = ({ children, node }) => {\n const collapsible = node.props.collapsible;\n const rememberState = node.props.rememberState;\n const identity = nodeIdentity(node);\n const storageKey = `lattice:sidebar:${identity ?? \"default\"}`;\n const isDesktop = useIsDesktop();\n\n const [collapsed, setCollapsed] = useState(() =>\n readStored(storageKey, collapsible && rememberState),\n );\n const [mobileOpen, setMobileOpen] = useState(false);\n\n useEffect(() => {\n function toggle(event: Event): void {\n if (!matchesTarget(event, identity)) {\n return;\n }\n\n if (window.matchMedia?.(DESKTOP_QUERY).matches ?? true) {\n if (!collapsible) {\n return;\n }\n\n setCollapsed((value) => {\n const next = !value;\n\n if (rememberState && typeof window !== \"undefined\") {\n window.localStorage.setItem(storageKey, String(next));\n }\n\n return next;\n });\n } else {\n setMobileOpen((open) => !open);\n }\n }\n\n window.addEventListener(LATTICE_EVENT.toggleSidebar, toggle);\n\n return () => window.removeEventListener(LATTICE_EVENT.toggleSidebar, toggle);\n }, [identity, collapsible, rememberState, storageKey]);\n\n useEffect(() => router.on(\"navigate\", () => setMobileOpen(false)), []);\n\n useEffect(() => {\n if (!mobileOpen) {\n return;\n }\n\n function close(event: KeyboardEvent): void {\n if (event.key === \"Escape\") {\n setMobileOpen(false);\n }\n }\n\n window.addEventListener(\"keydown\", close);\n\n return () => window.removeEventListener(\"keydown\", close);\n }, [mobileOpen]);\n\n const isCollapsed = collapsible && collapsed && isDesktop;\n\n return (\n <CollapsedContext.Provider value={isCollapsed}>\n {mobileOpen ? (\n <div\n aria-hidden=\"true\"\n className=\"fixed inset-0 z-lt-overlay bg-lt-overlay md:hidden\"\n data-test=\"sidebar-backdrop\"\n onClick={() => setMobileOpen(false)}\n />\n ) : null}\n <aside\n className={cn(\n \"fixed inset-y-0 left-0 z-lt-modal flex h-svh w-72 max-w-[80vw] shrink-0 flex-col gap-4 border-r border-lt-border bg-lt-bg p-4 transition-transform\",\n \"md:sticky md:top-0 md:z-auto md:max-w-none md:translate-x-0 md:transition-[width]\",\n mobileOpen ? \"translate-x-0\" : \"-translate-x-full\",\n isCollapsed\n ? \"md:w-16 md:overflow-visible\"\n : \"md:w-64 md:overflow-x-hidden md:overflow-y-auto\",\n )}\n data-collapsed={isCollapsed ? \"true\" : \"false\"}\n data-lattice-component={identity}\n data-test=\"sidebar\"\n >\n {children}\n </aside>\n </CollapsedContext.Provider>\n );\n};\n\nexport default SidebarComponent;\n"],"mappings":";;;;;;;;AAQA,IAAM,gBAAgB;AAEtB,SAAS,WAAW,KAAa,UAA4B;CAC3D,IAAI,CAAC,YAAY,OAAO,WAAW,aACjC,OAAO;CAGT,OAAO,OAAO,aAAa,QAAQ,GAAG,MAAM;AAC9C;AAEA,SAAS,cAAc,OAAc,UAAuC;CAC1E,MAAM,SAAU,MAA2C,QAAQ;CAEnE,OAAO,UAAU,QAAQ,WAAW;AACtC;AAEA,SAAS,eAAwB;CAC/B,MAAM,kBAAkB,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe;CACtF,MAAM,CAAC,WAAW,gBAAgB,eAChC,UAAU,IAAI,OAAO,WAAW,aAAa,EAAE,UAAU,IAC3D;CAEA,gBAAgB;EACd,IAAI,CAAC,UAAU,GACb;EAGF,MAAM,QAAQ,OAAO,WAAW,aAAa;EAC7C,MAAM,eAAqB,aAAa,MAAM,OAAO;EAErD,OAAO;EACP,MAAM,iBAAiB,UAAU,MAAM;EAEvC,aAAa,MAAM,oBAAoB,UAAU,MAAM;CACzD,GAAG,CAAC,CAAC;CAEL,OAAO;AACT;AAEA,IAAM,oBAAkD,EAAE,UAAU,WAAW;CAC7E,MAAM,cAAc,KAAK,MAAM;CAC/B,MAAM,gBAAgB,KAAK,MAAM;CACjC,MAAM,WAAW,aAAa,IAAI;CAClC,MAAM,aAAa,mBAAmB,YAAY;CAClD,MAAM,YAAY,aAAa;CAE/B,MAAM,CAAC,WAAW,gBAAgB,eAChC,WAAW,YAAY,eAAe,aAAa,CACrD;CACA,MAAM,CAAC,YAAY,iBAAiB,SAAS,KAAK;CAElD,gBAAgB;EACd,SAAS,OAAO,OAAoB;GAClC,IAAI,CAAC,cAAc,OAAO,QAAQ,GAChC;GAGF,IAAI,OAAO,aAAa,aAAa,EAAE,WAAW,MAAM;IACtD,IAAI,CAAC,aACH;IAGF,cAAc,UAAU;KACtB,MAAM,OAAO,CAAC;KAEd,IAAI,iBAAiB,OAAO,WAAW,aACrC,OAAO,aAAa,QAAQ,YAAY,OAAO,IAAI,CAAC;KAGtD,OAAO;IACT,CAAC;GACH,OACE,eAAe,SAAS,CAAC,IAAI;EAEjC;EAEA,OAAO,iBAAiB,cAAc,eAAe,MAAM;EAE3D,aAAa,OAAO,oBAAoB,cAAc,eAAe,MAAM;CAC7E,GAAG;EAAC;EAAU;EAAa;EAAe;CAAU,CAAC;CAErD,gBAAgB,OAAO,GAAG,kBAAkB,cAAc,KAAK,CAAC,GAAG,CAAC,CAAC;CAErE,gBAAgB;EACd,IAAI,CAAC,YACH;EAGF,SAAS,MAAM,OAA4B;GACzC,IAAI,MAAM,QAAQ,UAChB,cAAc,KAAK;EAEvB;EAEA,OAAO,iBAAiB,WAAW,KAAK;EAExC,aAAa,OAAO,oBAAoB,WAAW,KAAK;CAC1D,GAAG,CAAC,UAAU,CAAC;CAEf,MAAM,cAAc,eAAe,aAAa;CAEhD,OACE,qBAAC,iBAAiB,UAAlB;EAA2B,OAAO;YAAlC,CACG,aACC,oBAAC,OAAD;GACE,eAAY;GACZ,WAAU;GACV,aAAU;GACV,eAAe,cAAc,KAAK;EACnC,CAAA,IACC,MACJ,oBAAC,SAAD;GACE,WAAW,GACT,sJACA,qFACA,aAAa,kBAAkB,qBAC/B,cACI,gCACA,iDACN;GACA,kBAAgB,cAAc,SAAS;GACvC,0BAAwB;GACxB,aAAU;GAET;EACI,CAAA,CACkB;;AAE/B"}
|
package/dist/layout/topbar.js
CHANGED
|
@@ -6,7 +6,7 @@ var TopbarComponent = ({ children, node }) => {
|
|
|
6
6
|
const sticky = node.props.sticky;
|
|
7
7
|
return /* @__PURE__ */ jsx("header", {
|
|
8
8
|
"data-lattice-component": nodeIdentity(node),
|
|
9
|
-
className: cn("flex h-14 w-full items-center gap-2 border-b border-lt-border bg-lt-bg px-4 text-lt-fg", sticky && "sticky top-0 z-
|
|
9
|
+
className: cn("flex h-14 w-full items-center gap-2 border-b border-lt-border bg-lt-bg px-4 text-lt-fg", sticky && "sticky top-0 z-lt-sticky"),
|
|
10
10
|
children
|
|
11
11
|
});
|
|
12
12
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"topbar.js","names":[],"sources":["../../resources/js/layout/topbar.tsx"],"sourcesContent":["import type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { nodeIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\n\nconst TopbarComponent: RendererComponent<\"topbar\"> = ({ children, node }) => {\n const sticky = node.props.sticky;\n\n return (\n <header\n data-lattice-component={nodeIdentity(node)}\n className={cn(\n \"flex h-14 w-full items-center gap-2 border-b border-lt-border bg-lt-bg px-4 text-lt-fg\",\n sticky && \"sticky top-0 z-
|
|
1
|
+
{"version":3,"file":"topbar.js","names":[],"sources":["../../resources/js/layout/topbar.tsx"],"sourcesContent":["import type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { nodeIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\n\nconst TopbarComponent: RendererComponent<\"topbar\"> = ({ children, node }) => {\n const sticky = node.props.sticky;\n\n return (\n <header\n data-lattice-component={nodeIdentity(node)}\n className={cn(\n \"flex h-14 w-full items-center gap-2 border-b border-lt-border bg-lt-bg px-4 text-lt-fg\",\n sticky && \"sticky top-0 z-lt-sticky\",\n )}\n >\n {children}\n </header>\n );\n};\n\nexport default TopbarComponent;\n"],"mappings":";;;;AAIA,IAAM,mBAAgD,EAAE,UAAU,WAAW;CAC3E,MAAM,SAAS,KAAK,MAAM;CAE1B,OACE,oBAAC,UAAD;EACE,0BAAwB,aAAa,IAAI;EACzC,WAAW,GACT,0FACA,UAAU,0BACZ;EAEC;CACK,CAAA;AAEZ"}
|
|
@@ -1,25 +1,24 @@
|
|
|
1
1
|
import { useLocale } from "../../../i18n/locale.js";
|
|
2
2
|
import { formatCell } from "../../format.js";
|
|
3
|
-
import { numericValue } from "
|
|
3
|
+
import { numericValue } from "../../../format/numeric.js";
|
|
4
|
+
import { formatNumber } from "../../../format/number.js";
|
|
4
5
|
import { jsx } from "react/jsx-runtime";
|
|
5
6
|
//#region resources/js/table/components/cells/money-cell.tsx
|
|
6
7
|
var MoneyCell = ({ column, props, row, value }) => {
|
|
7
8
|
const { locale } = useLocale();
|
|
8
|
-
|
|
9
|
-
if (number === null) return /* @__PURE__ */ jsx("span", { children: formatCell(value, column) });
|
|
9
|
+
if (numericValue(value) === null) return /* @__PURE__ */ jsx("span", { children: formatCell(value, column) });
|
|
10
10
|
const rawCode = props.currencyField ? row[props.currencyField] : void 0;
|
|
11
|
-
const code = props.currency ?? (typeof rawCode === "string" ? rawCode :
|
|
12
|
-
const fractionDigits = {
|
|
13
|
-
minimumFractionDigits: props.minimumFractionDigits ?? void 0,
|
|
14
|
-
maximumFractionDigits: props.maximumFractionDigits ?? void 0
|
|
15
|
-
};
|
|
11
|
+
const code = props.currency ?? (typeof rawCode === "string" ? rawCode : null);
|
|
16
12
|
return /* @__PURE__ */ jsx("span", {
|
|
17
13
|
className: "tabular-nums",
|
|
18
|
-
children:
|
|
19
|
-
|
|
14
|
+
children: formatNumber(value, {
|
|
15
|
+
kind: "number",
|
|
16
|
+
notation: "standard",
|
|
17
|
+
minimumFractionDigits: props.minimumFractionDigits,
|
|
18
|
+
maximumFractionDigits: props.maximumFractionDigits,
|
|
20
19
|
currency: code,
|
|
21
|
-
|
|
22
|
-
}
|
|
20
|
+
unit: null
|
|
21
|
+
}, locale)
|
|
23
22
|
});
|
|
24
23
|
};
|
|
25
24
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"money-cell.js","names":[],"sources":["../../../../resources/js/table/components/cells/money-cell.tsx"],"sourcesContent":["import { useLocale } from \"@lattice-php/lattice/i18n\";\nimport {
|
|
1
|
+
{"version":3,"file":"money-cell.js","names":[],"sources":["../../../../resources/js/table/components/cells/money-cell.tsx"],"sourcesContent":["import { useLocale } from \"@lattice-php/lattice/i18n\";\nimport { formatNumber } from \"../../../format/number\";\nimport { numericValue } from \"../../../format/numeric\";\nimport { formatCell } from \"../../format\";\nimport type { ColumnCellComponent } from \"../../registry\";\n\nexport const MoneyCell: ColumnCellComponent<\"column.money\"> = ({ column, props, row, value }) => {\n const { locale } = useLocale();\n\n if (numericValue(value) === null) {\n return <span>{formatCell(value, column)}</span>;\n }\n\n const rawCode = props.currencyField ? row[props.currencyField] : undefined;\n const code = props.currency ?? (typeof rawCode === \"string\" ? rawCode : null);\n\n const text = formatNumber(\n value,\n {\n kind: \"number\",\n notation: \"standard\",\n minimumFractionDigits: props.minimumFractionDigits,\n maximumFractionDigits: props.maximumFractionDigits,\n currency: code,\n unit: null,\n },\n locale,\n );\n\n return <span className=\"tabular-nums\">{text}</span>;\n};\n"],"mappings":";;;;;;AAMA,IAAa,aAAkD,EAAE,QAAQ,OAAO,KAAK,YAAY;CAC/F,MAAM,EAAE,WAAW,UAAU;CAE7B,IAAI,aAAa,KAAK,MAAM,MAC1B,OAAO,oBAAC,QAAD,EAAA,UAAO,WAAW,OAAO,MAAM,EAAQ,CAAA;CAGhD,MAAM,UAAU,MAAM,gBAAgB,IAAI,MAAM,iBAAiB,KAAA;CACjE,MAAM,OAAO,MAAM,aAAa,OAAO,YAAY,WAAW,UAAU;CAexE,OAAO,oBAAC,QAAD;EAAM,WAAU;YAbV,aACX,OACA;GACE,MAAM;GACN,UAAU;GACV,uBAAuB,MAAM;GAC7B,uBAAuB,MAAM;GAC7B,UAAU;GACV,MAAM;EACR,GACA,MAGqC;CAAW,CAAA;AACpD"}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import { useLocale } from "../../../i18n/locale.js";
|
|
2
2
|
import { formatCell } from "../../format.js";
|
|
3
|
-
import { numericValue } from "
|
|
3
|
+
import { numericValue } from "../../../format/numeric.js";
|
|
4
|
+
import { formatNumber } from "../../../format/number.js";
|
|
4
5
|
import { jsx } from "react/jsx-runtime";
|
|
5
6
|
//#region resources/js/table/components/cells/number-cell.tsx
|
|
6
7
|
var NumberCell = ({ column, props, value }) => {
|
|
7
8
|
const { locale } = useLocale();
|
|
8
|
-
|
|
9
|
-
if (number === null) return /* @__PURE__ */ jsx("span", { children: formatCell(value, column) });
|
|
9
|
+
if (numericValue(value) === null) return /* @__PURE__ */ jsx("span", { children: formatCell(value, column) });
|
|
10
10
|
return /* @__PURE__ */ jsx("span", {
|
|
11
11
|
className: "tabular-nums",
|
|
12
|
-
children:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
})
|
|
12
|
+
children: formatNumber(value, {
|
|
13
|
+
kind: "number",
|
|
14
|
+
notation: props.compact ? "compact" : "standard",
|
|
15
|
+
minimumFractionDigits: props.minimumFractionDigits,
|
|
16
|
+
maximumFractionDigits: props.maximumFractionDigits,
|
|
17
|
+
currency: null,
|
|
18
|
+
unit: props.unit
|
|
19
|
+
}, locale)
|
|
20
20
|
});
|
|
21
21
|
};
|
|
22
22
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"number-cell.js","names":[],"sources":["../../../../resources/js/table/components/cells/number-cell.tsx"],"sourcesContent":["import { useLocale } from \"@lattice-php/lattice/i18n\";\nimport {
|
|
1
|
+
{"version":3,"file":"number-cell.js","names":[],"sources":["../../../../resources/js/table/components/cells/number-cell.tsx"],"sourcesContent":["import { useLocale } from \"@lattice-php/lattice/i18n\";\nimport { formatNumber } from \"../../../format/number\";\nimport { numericValue } from \"../../../format/numeric\";\nimport { formatCell } from \"../../format\";\nimport type { ColumnCellComponent } from \"../../registry\";\n\nexport const NumberCell: ColumnCellComponent<\"column.number\"> = ({ column, props, value }) => {\n const { locale } = useLocale();\n\n if (numericValue(value) === null) {\n return <span>{formatCell(value, column)}</span>;\n }\n\n const text = formatNumber(\n value,\n {\n kind: \"number\",\n notation: props.compact ? \"compact\" : \"standard\",\n minimumFractionDigits: props.minimumFractionDigits,\n maximumFractionDigits: props.maximumFractionDigits,\n currency: null,\n unit: props.unit,\n },\n locale,\n );\n\n return <span className=\"tabular-nums\">{text}</span>;\n};\n"],"mappings":";;;;;;AAMA,IAAa,cAAoD,EAAE,QAAQ,OAAO,YAAY;CAC5F,MAAM,EAAE,WAAW,UAAU;CAE7B,IAAI,aAAa,KAAK,MAAM,MAC1B,OAAO,oBAAC,QAAD,EAAA,UAAO,WAAW,OAAO,MAAM,EAAQ,CAAA;CAgBhD,OAAO,oBAAC,QAAD;EAAM,WAAU;YAbV,aACX,OACA;GACE,MAAM;GACN,UAAU,MAAM,UAAU,YAAY;GACtC,uBAAuB,MAAM;GAC7B,uBAAuB,MAAM;GAC7B,UAAU;GACV,MAAM,MAAM;EACd,GACA,MAGqC;CAAW,CAAA;AACpD"}
|
|
@@ -59,7 +59,7 @@ function ColumnFilterControl({ column, clauses, processing, onAdd, onUpdate, onR
|
|
|
59
59
|
type: "button",
|
|
60
60
|
"aria-label": t("table.filter.columnFilters", "{{label}} filters", { label: column.label }),
|
|
61
61
|
"data-test": `filter-${column.key}`,
|
|
62
|
-
className: "relative -ml-px inline-flex size-
|
|
62
|
+
className: "relative -ml-px inline-flex size-lt-control-md shrink-0 items-center justify-center rounded-r-lt-sm border border-lt-input disabled:opacity-50 data-[state=open]:z-10 data-[state=open]:border-lt-primary",
|
|
63
63
|
disabled: processing,
|
|
64
64
|
children: [/* @__PURE__ */ jsx(Icon, {
|
|
65
65
|
name: "filter",
|
|
@@ -252,7 +252,7 @@ function FilterClauseRow({ column, type, operators, clause, processing, onOperat
|
|
|
252
252
|
type: "button",
|
|
253
253
|
"aria-label": t("table.filter.remove", "Remove {{label}} filter", { label: column.label }),
|
|
254
254
|
"data-test": `filter-${column.key}-remove`,
|
|
255
|
-
className: "inline-flex size-
|
|
255
|
+
className: "inline-flex size-lt-control-md items-center justify-center rounded-lt-sm border border-lt-border hover:bg-lt-muted disabled:opacity-50",
|
|
256
256
|
disabled: processing,
|
|
257
257
|
onClick: onRemove,
|
|
258
258
|
children: /* @__PURE__ */ jsx(Icon, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"column-filter-control.js","names":[],"sources":["../../../resources/js/table/components/column-filter-control.tsx"],"sourcesContent":["import { Icon } from \"@lattice-php/lattice/icons\";\nimport { useState } from \"react\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@lattice-php/lattice/core/components/popover\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport type {\n ColumnFilterOption,\n FilterData,\n FilterType,\n Op,\n} from \"@lattice-php/lattice/types/generated\";\nimport { operatorLabel, VALUELESS_FILTER_OPERATORS } from \"../query\";\nimport type { FilterClause, TableColumn } from \"../types\";\nimport { type FilterOptionSearch, TableFilterControl } from \"./filter-controls\";\nimport { fieldClass, FilterValueInput } from \"./filter-value-input\";\n\ntype ColumnClause = { clause: FilterClause; index: number };\n\nexport function ColumnFilterControl({\n column,\n clauses,\n processing,\n onAdd,\n onUpdate,\n onRemove,\n onReplace,\n onSearch,\n}: {\n column: TableColumn;\n clauses: ColumnClause[];\n processing: boolean;\n onAdd: (clause: FilterClause) => void;\n onUpdate: (index: number, clause: FilterClause) => void;\n onRemove: (index: number) => void;\n onReplace: (field: string, clauses: FilterClause[]) => void;\n onSearch?: FilterOptionSearch;\n}) {\n const { t } = useT(\"lattice\");\n const filter = column.filter;\n\n if (!filter) {\n return null;\n }\n\n if (filter.control === \"select\") {\n return (\n <ColumnSelectFilter\n column={column}\n clauses={clauses}\n processing={processing}\n onReplace={onReplace}\n onSearch={onSearch}\n />\n );\n }\n\n const type = filter.type ?? \"text\";\n const operators = filter.operators ?? [];\n const defaultOperator = filter.defaultOperator ?? operators[0] ?? \"eq\";\n const primary = clauses.find((entry) => entry.clause.operator === defaultOperator) ?? clauses[0];\n\n function commitPrimary(value: string): void {\n if (value === \"\") {\n if (primary) {\n onRemove(primary.index);\n }\n\n return;\n }\n\n if (primary) {\n onUpdate(primary.index, { ...primary.clause, value });\n } else {\n onAdd({ field: column.key, operator: defaultOperator, value });\n }\n }\n\n return (\n <div className=\"flex min-w-0 max-w-80 items-stretch\">\n <div className=\"min-w-0 flex-1\">\n <FilterValueInput\n type={type}\n label={column.label}\n value={primary?.clause.value ?? \"\"}\n processing={processing}\n withSearchIcon={type === \"text\" || type === \"number\"}\n grouped\n testId={`filter-${column.key}-value`}\n onCommit={commitPrimary}\n onClear={primary ? () => onRemove(primary.index) : undefined}\n />\n </div>\n <Popover>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n aria-label={t(\"table.filter.columnFilters\", \"{{label}} filters\", {\n label: column.label,\n })}\n data-test={`filter-${column.key}`}\n className=\"relative -ml-px inline-flex size-9 shrink-0 items-center justify-center rounded-r-lt-sm border border-lt-input disabled:opacity-50 data-[state=open]:z-10 data-[state=open]:border-lt-primary\"\n disabled={processing}\n >\n <Icon name=\"filter\" aria-hidden=\"true\" className=\"size-lt-icon-md\" />\n {clauses.length > 0 && (\n <span className=\"absolute -right-1.5 -top-1.5 inline-flex size-4 items-center justify-center rounded-full bg-lt-primary text-xs font-medium text-lt-primary-fg\">\n {clauses.length}\n </span>\n )}\n </button>\n </PopoverTrigger>\n\n <PopoverContent align=\"start\" className=\"w-80 p-4\">\n <FilterClauseList\n column={column}\n clauses={clauses}\n operators={operators}\n defaultOperator={defaultOperator}\n processing={processing}\n onAdd={onAdd}\n onUpdate={onUpdate}\n onRemove={onRemove}\n />\n </PopoverContent>\n </Popover>\n </div>\n );\n}\n\nfunction serializeColumnValue(value: unknown): string {\n if (Array.isArray(value)) {\n return value.join(\",\");\n }\n\n return typeof value === \"string\" ? value : \"\";\n}\n\nfunction ColumnSelectFilter({\n column,\n clauses,\n processing,\n onReplace,\n onSearch,\n}: {\n column: TableColumn;\n clauses: ColumnClause[];\n processing: boolean;\n onReplace: (field: string, clauses: FilterClause[]) => void;\n onSearch?: FilterOptionSearch;\n}) {\n const filter = column.filter;\n\n if (!filter) {\n return null;\n }\n\n const multiple = filter.multiple;\n const operator = filter.defaultOperator;\n const clauseOptions = filter.clauseOptions ?? [];\n const activeClauseOption = findActiveClauseOption(\n clauses.map((entry) => entry.clause),\n clauseOptions,\n );\n const active = clauses.find((entry) => entry.clause.operator === operator) ?? clauses[0];\n const value: unknown = activeClauseOption\n ? activeClauseOption.value\n : multiple\n ? active?.clause.value\n ? active.clause.value.split(\",\")\n : []\n : (active?.clause.value ?? \"\");\n\n const data: FilterData = {\n key: column.key,\n label: column.label,\n type: \"select\",\n props: { options: filter.options, multiple, searchable: filter.searchable, placeholder: null },\n };\n\n function change(next: unknown): void {\n const serialized = serializeColumnValue(next);\n\n if (serialized === \"\") {\n onReplace(column.key, []);\n\n return;\n }\n\n const clauseOption = clauseOptions.find((option) => option.value === serialized);\n\n if (clauseOption) {\n onReplace(column.key, clausesForOption(column.key, clauseOption));\n\n return;\n }\n\n onReplace(column.key, [{ field: column.key, operator, value: serialized }]);\n }\n\n return (\n <TableFilterControl\n filter={data}\n value={value}\n processing={processing}\n onChange={change}\n onSearch={onSearch}\n />\n );\n}\n\nfunction clausesForOption(field: string, option: ColumnFilterOption): FilterClause[] {\n return option.clauses.map((clause) => ({\n field,\n operator: clause.operator,\n value: clause.value,\n }));\n}\n\nfunction findActiveClauseOption(\n clauses: FilterClause[],\n options: ColumnFilterOption[],\n): ColumnFilterOption | undefined {\n return options.find((option) => clausesMatch(clauses, clausesForOption(\"\", option)));\n}\n\nfunction clausesMatch(active: FilterClause[], expected: FilterClause[]): boolean {\n if (active.length !== expected.length) {\n return false;\n }\n\n return expected.every((clause) =>\n active.some(\n (current) => current.operator === clause.operator && current.value === clause.value,\n ),\n );\n}\n\nfunction FilterClauseList({\n column,\n clauses,\n operators,\n defaultOperator,\n processing,\n onAdd,\n onUpdate,\n onRemove,\n}: {\n column: TableColumn;\n clauses: ColumnClause[];\n operators: Op[];\n defaultOperator: Op;\n processing: boolean;\n onAdd: (clause: FilterClause) => void;\n onUpdate: (index: number, clause: FilterClause) => void;\n onRemove: (index: number) => void;\n}) {\n const { t } = useT(\"lattice\");\n const type = column.filter?.type ?? \"text\";\n const [draftOperator, setDraftOperator] = useState(defaultOperator);\n const [adding, setAdding] = useState(clauses.length === 0);\n\n return (\n <div className=\"grid gap-3\">\n {clauses.map((entry) => (\n <FilterClauseRow\n key={entry.index}\n column={column}\n type={type}\n operators={operators}\n clause={entry.clause}\n processing={processing}\n onOperator={(operator) => onUpdate(entry.index, { ...entry.clause, operator })}\n onValue={(value) =>\n value === \"\" ? onRemove(entry.index) : onUpdate(entry.index, { ...entry.clause, value })\n }\n onRemove={() => onRemove(entry.index)}\n />\n ))}\n\n {adding && (\n <FilterClauseRow\n column={column}\n type={type}\n operators={operators}\n clause={{ field: column.key, operator: draftOperator, value: \"\" }}\n processing={processing}\n onOperator={(operator) => {\n if (VALUELESS_FILTER_OPERATORS.has(operator)) {\n onAdd({ field: column.key, operator, value: \"\" });\n setDraftOperator(defaultOperator);\n setAdding(false);\n return;\n }\n setDraftOperator(operator);\n }}\n onValue={(value) => {\n if (value !== \"\") {\n onAdd({ field: column.key, operator: draftOperator, value });\n setDraftOperator(defaultOperator);\n setAdding(false);\n }\n }}\n onRemove={() => setAdding(false)}\n />\n )}\n\n <div className=\"border-t border-lt-border pt-3\">\n <button\n type=\"button\"\n data-test={`filter-${column.key}-add`}\n className=\"inline-flex w-full items-center justify-center gap-2 rounded-lt-sm bg-lt-muted px-3 py-2 text-sm font-medium hover:bg-lt-muted/70 disabled:opacity-50\"\n disabled={processing}\n onClick={() => setAdding(true)}\n >\n <Icon name=\"plus\" aria-hidden=\"true\" className=\"size-lt-icon-md\" />\n {t(\"table.filter.add\", \"Add filter\")}\n </button>\n </div>\n </div>\n );\n}\n\nfunction FilterClauseRow({\n column,\n type,\n operators,\n clause,\n processing,\n onOperator,\n onValue,\n onRemove,\n}: {\n column: TableColumn;\n type: FilterType;\n operators: Op[];\n clause: FilterClause;\n processing: boolean;\n onOperator: (operator: Op) => void;\n onValue: (value: string) => void;\n onRemove: () => void;\n}) {\n const { t } = useT(\"lattice\");\n const valueless = VALUELESS_FILTER_OPERATORS.has(clause.operator);\n\n return (\n <div className=\"grid gap-2\">\n <div className=\"flex items-center gap-2\">\n {operators.length > 1 ? (\n <select\n aria-label={t(\"table.filter.operator\", \"{{label}} operator\", { label: column.label })}\n data-test={`filter-${column.key}-operator`}\n className={cn(fieldClass, \"flex-1\")}\n disabled={processing}\n value={clause.operator}\n onChange={(event) => onOperator(event.target.value as Op)}\n >\n {operators.map((operator) => (\n <option key={operator} value={operator}>\n {operatorLabel(operator)}\n </option>\n ))}\n </select>\n ) : (\n <span className=\"flex-1 text-sm font-medium\">{operatorLabel(clause.operator)}</span>\n )}\n <button\n type=\"button\"\n aria-label={t(\"table.filter.remove\", \"Remove {{label}} filter\", { label: column.label })}\n data-test={`filter-${column.key}-remove`}\n className=\"inline-flex size-9 items-center justify-center rounded-lt-sm border border-lt-border hover:bg-lt-muted disabled:opacity-50\"\n disabled={processing}\n onClick={onRemove}\n >\n <Icon name=\"trash-2\" aria-hidden=\"true\" className=\"size-lt-icon-md\" />\n </button>\n </div>\n {!valueless && (\n <FilterValueInput\n type={type}\n label={column.label}\n ariaLabel={t(\"table.filter.value\", \"{{label}} filter value\", { label: column.label })}\n value={clause.value}\n processing={processing}\n onCommit={onValue}\n onClear={() => onValue(\"\")}\n />\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"column-filter-control.js","names":[],"sources":["../../../resources/js/table/components/column-filter-control.tsx"],"sourcesContent":["import { Icon } from \"@lattice-php/lattice/icons\";\nimport { useState } from \"react\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@lattice-php/lattice/core/components/popover\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport type {\n ColumnFilterOption,\n FilterData,\n FilterType,\n Op,\n} from \"@lattice-php/lattice/types/generated\";\nimport { operatorLabel, VALUELESS_FILTER_OPERATORS } from \"../query\";\nimport type { FilterClause, TableColumn } from \"../types\";\nimport { type FilterOptionSearch, TableFilterControl } from \"./filter-controls\";\nimport { fieldClass, FilterValueInput } from \"./filter-value-input\";\n\ntype ColumnClause = { clause: FilterClause; index: number };\n\nexport function ColumnFilterControl({\n column,\n clauses,\n processing,\n onAdd,\n onUpdate,\n onRemove,\n onReplace,\n onSearch,\n}: {\n column: TableColumn;\n clauses: ColumnClause[];\n processing: boolean;\n onAdd: (clause: FilterClause) => void;\n onUpdate: (index: number, clause: FilterClause) => void;\n onRemove: (index: number) => void;\n onReplace: (field: string, clauses: FilterClause[]) => void;\n onSearch?: FilterOptionSearch;\n}) {\n const { t } = useT(\"lattice\");\n const filter = column.filter;\n\n if (!filter) {\n return null;\n }\n\n if (filter.control === \"select\") {\n return (\n <ColumnSelectFilter\n column={column}\n clauses={clauses}\n processing={processing}\n onReplace={onReplace}\n onSearch={onSearch}\n />\n );\n }\n\n const type = filter.type ?? \"text\";\n const operators = filter.operators ?? [];\n const defaultOperator = filter.defaultOperator ?? operators[0] ?? \"eq\";\n const primary = clauses.find((entry) => entry.clause.operator === defaultOperator) ?? clauses[0];\n\n function commitPrimary(value: string): void {\n if (value === \"\") {\n if (primary) {\n onRemove(primary.index);\n }\n\n return;\n }\n\n if (primary) {\n onUpdate(primary.index, { ...primary.clause, value });\n } else {\n onAdd({ field: column.key, operator: defaultOperator, value });\n }\n }\n\n return (\n <div className=\"flex min-w-0 max-w-80 items-stretch\">\n <div className=\"min-w-0 flex-1\">\n <FilterValueInput\n type={type}\n label={column.label}\n value={primary?.clause.value ?? \"\"}\n processing={processing}\n withSearchIcon={type === \"text\" || type === \"number\"}\n grouped\n testId={`filter-${column.key}-value`}\n onCommit={commitPrimary}\n onClear={primary ? () => onRemove(primary.index) : undefined}\n />\n </div>\n <Popover>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n aria-label={t(\"table.filter.columnFilters\", \"{{label}} filters\", {\n label: column.label,\n })}\n data-test={`filter-${column.key}`}\n className=\"relative -ml-px inline-flex size-lt-control-md shrink-0 items-center justify-center rounded-r-lt-sm border border-lt-input disabled:opacity-50 data-[state=open]:z-10 data-[state=open]:border-lt-primary\"\n disabled={processing}\n >\n <Icon name=\"filter\" aria-hidden=\"true\" className=\"size-lt-icon-md\" />\n {clauses.length > 0 && (\n <span className=\"absolute -right-1.5 -top-1.5 inline-flex size-4 items-center justify-center rounded-full bg-lt-primary text-xs font-medium text-lt-primary-fg\">\n {clauses.length}\n </span>\n )}\n </button>\n </PopoverTrigger>\n\n <PopoverContent align=\"start\" className=\"w-80 p-4\">\n <FilterClauseList\n column={column}\n clauses={clauses}\n operators={operators}\n defaultOperator={defaultOperator}\n processing={processing}\n onAdd={onAdd}\n onUpdate={onUpdate}\n onRemove={onRemove}\n />\n </PopoverContent>\n </Popover>\n </div>\n );\n}\n\nfunction serializeColumnValue(value: unknown): string {\n if (Array.isArray(value)) {\n return value.join(\",\");\n }\n\n return typeof value === \"string\" ? value : \"\";\n}\n\nfunction ColumnSelectFilter({\n column,\n clauses,\n processing,\n onReplace,\n onSearch,\n}: {\n column: TableColumn;\n clauses: ColumnClause[];\n processing: boolean;\n onReplace: (field: string, clauses: FilterClause[]) => void;\n onSearch?: FilterOptionSearch;\n}) {\n const filter = column.filter;\n\n if (!filter) {\n return null;\n }\n\n const multiple = filter.multiple;\n const operator = filter.defaultOperator;\n const clauseOptions = filter.clauseOptions ?? [];\n const activeClauseOption = findActiveClauseOption(\n clauses.map((entry) => entry.clause),\n clauseOptions,\n );\n const active = clauses.find((entry) => entry.clause.operator === operator) ?? clauses[0];\n const value: unknown = activeClauseOption\n ? activeClauseOption.value\n : multiple\n ? active?.clause.value\n ? active.clause.value.split(\",\")\n : []\n : (active?.clause.value ?? \"\");\n\n const data: FilterData = {\n key: column.key,\n label: column.label,\n type: \"select\",\n props: { options: filter.options, multiple, searchable: filter.searchable, placeholder: null },\n };\n\n function change(next: unknown): void {\n const serialized = serializeColumnValue(next);\n\n if (serialized === \"\") {\n onReplace(column.key, []);\n\n return;\n }\n\n const clauseOption = clauseOptions.find((option) => option.value === serialized);\n\n if (clauseOption) {\n onReplace(column.key, clausesForOption(column.key, clauseOption));\n\n return;\n }\n\n onReplace(column.key, [{ field: column.key, operator, value: serialized }]);\n }\n\n return (\n <TableFilterControl\n filter={data}\n value={value}\n processing={processing}\n onChange={change}\n onSearch={onSearch}\n />\n );\n}\n\nfunction clausesForOption(field: string, option: ColumnFilterOption): FilterClause[] {\n return option.clauses.map((clause) => ({\n field,\n operator: clause.operator,\n value: clause.value,\n }));\n}\n\nfunction findActiveClauseOption(\n clauses: FilterClause[],\n options: ColumnFilterOption[],\n): ColumnFilterOption | undefined {\n return options.find((option) => clausesMatch(clauses, clausesForOption(\"\", option)));\n}\n\nfunction clausesMatch(active: FilterClause[], expected: FilterClause[]): boolean {\n if (active.length !== expected.length) {\n return false;\n }\n\n return expected.every((clause) =>\n active.some(\n (current) => current.operator === clause.operator && current.value === clause.value,\n ),\n );\n}\n\nfunction FilterClauseList({\n column,\n clauses,\n operators,\n defaultOperator,\n processing,\n onAdd,\n onUpdate,\n onRemove,\n}: {\n column: TableColumn;\n clauses: ColumnClause[];\n operators: Op[];\n defaultOperator: Op;\n processing: boolean;\n onAdd: (clause: FilterClause) => void;\n onUpdate: (index: number, clause: FilterClause) => void;\n onRemove: (index: number) => void;\n}) {\n const { t } = useT(\"lattice\");\n const type = column.filter?.type ?? \"text\";\n const [draftOperator, setDraftOperator] = useState(defaultOperator);\n const [adding, setAdding] = useState(clauses.length === 0);\n\n return (\n <div className=\"grid gap-3\">\n {clauses.map((entry) => (\n <FilterClauseRow\n key={entry.index}\n column={column}\n type={type}\n operators={operators}\n clause={entry.clause}\n processing={processing}\n onOperator={(operator) => onUpdate(entry.index, { ...entry.clause, operator })}\n onValue={(value) =>\n value === \"\" ? onRemove(entry.index) : onUpdate(entry.index, { ...entry.clause, value })\n }\n onRemove={() => onRemove(entry.index)}\n />\n ))}\n\n {adding && (\n <FilterClauseRow\n column={column}\n type={type}\n operators={operators}\n clause={{ field: column.key, operator: draftOperator, value: \"\" }}\n processing={processing}\n onOperator={(operator) => {\n if (VALUELESS_FILTER_OPERATORS.has(operator)) {\n onAdd({ field: column.key, operator, value: \"\" });\n setDraftOperator(defaultOperator);\n setAdding(false);\n return;\n }\n setDraftOperator(operator);\n }}\n onValue={(value) => {\n if (value !== \"\") {\n onAdd({ field: column.key, operator: draftOperator, value });\n setDraftOperator(defaultOperator);\n setAdding(false);\n }\n }}\n onRemove={() => setAdding(false)}\n />\n )}\n\n <div className=\"border-t border-lt-border pt-3\">\n <button\n type=\"button\"\n data-test={`filter-${column.key}-add`}\n className=\"inline-flex w-full items-center justify-center gap-2 rounded-lt-sm bg-lt-muted px-3 py-2 text-sm font-medium hover:bg-lt-muted/70 disabled:opacity-50\"\n disabled={processing}\n onClick={() => setAdding(true)}\n >\n <Icon name=\"plus\" aria-hidden=\"true\" className=\"size-lt-icon-md\" />\n {t(\"table.filter.add\", \"Add filter\")}\n </button>\n </div>\n </div>\n );\n}\n\nfunction FilterClauseRow({\n column,\n type,\n operators,\n clause,\n processing,\n onOperator,\n onValue,\n onRemove,\n}: {\n column: TableColumn;\n type: FilterType;\n operators: Op[];\n clause: FilterClause;\n processing: boolean;\n onOperator: (operator: Op) => void;\n onValue: (value: string) => void;\n onRemove: () => void;\n}) {\n const { t } = useT(\"lattice\");\n const valueless = VALUELESS_FILTER_OPERATORS.has(clause.operator);\n\n return (\n <div className=\"grid gap-2\">\n <div className=\"flex items-center gap-2\">\n {operators.length > 1 ? (\n <select\n aria-label={t(\"table.filter.operator\", \"{{label}} operator\", { label: column.label })}\n data-test={`filter-${column.key}-operator`}\n className={cn(fieldClass, \"flex-1\")}\n disabled={processing}\n value={clause.operator}\n onChange={(event) => onOperator(event.target.value as Op)}\n >\n {operators.map((operator) => (\n <option key={operator} value={operator}>\n {operatorLabel(operator)}\n </option>\n ))}\n </select>\n ) : (\n <span className=\"flex-1 text-sm font-medium\">{operatorLabel(clause.operator)}</span>\n )}\n <button\n type=\"button\"\n aria-label={t(\"table.filter.remove\", \"Remove {{label}} filter\", { label: column.label })}\n data-test={`filter-${column.key}-remove`}\n className=\"inline-flex size-lt-control-md items-center justify-center rounded-lt-sm border border-lt-border hover:bg-lt-muted disabled:opacity-50\"\n disabled={processing}\n onClick={onRemove}\n >\n <Icon name=\"trash-2\" aria-hidden=\"true\" className=\"size-lt-icon-md\" />\n </button>\n </div>\n {!valueless && (\n <FilterValueInput\n type={type}\n label={column.label}\n ariaLabel={t(\"table.filter.value\", \"{{label}} filter value\", { label: column.label })}\n value={clause.value}\n processing={processing}\n onCommit={onValue}\n onClear={() => onValue(\"\")}\n />\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -101,7 +101,7 @@ function MultiSelectControl({ filter, value, processing, onChange }) {
|
|
|
101
101
|
className: "size-lt-icon-sm shrink-0"
|
|
102
102
|
})]
|
|
103
103
|
}), open && /* @__PURE__ */ jsx("div", {
|
|
104
|
-
className: "absolute z-
|
|
104
|
+
className: "absolute z-lt-dropdown mt-1 min-w-full rounded-lt-sm border border-lt-border bg-lt-bg p-1 shadow-lt-md",
|
|
105
105
|
children: filterOptions(filter).map((option) => /* @__PURE__ */ jsxs("label", {
|
|
106
106
|
className: "flex cursor-pointer items-center gap-2 rounded-lt-sm px-2 py-1.5 text-sm hover:bg-lt-muted",
|
|
107
107
|
children: [/* @__PURE__ */ jsx(Checkbox, {
|
|
@@ -240,7 +240,7 @@ function DateRangeControl({ filter, value, processing, onChange }) {
|
|
|
240
240
|
function ToggleControl({ filter, value, processing, onChange }) {
|
|
241
241
|
const checked = value === "1" || value === true || value === "true";
|
|
242
242
|
return /* @__PURE__ */ jsxs("label", {
|
|
243
|
-
className: "flex h-
|
|
243
|
+
className: "flex h-lt-control-md cursor-pointer items-center gap-2 text-sm",
|
|
244
244
|
children: [/* @__PURE__ */ jsx(Checkbox, {
|
|
245
245
|
"aria-label": filter.label,
|
|
246
246
|
"data-test": `table-filter-${filter.key}`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filter-controls.js","names":[],"sources":["../../../resources/js/table/components/filter-controls.tsx"],"sourcesContent":["import { Icon } from \"@lattice-php/lattice/icons\";\nimport { useState } from \"react\";\nimport { Checkbox } from \"@lattice-php/lattice/core/components/checkbox\";\nimport { Combobox } from \"@lattice-php/lattice/core/components/combobox\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport type { FilterData, Option } from \"@lattice-php/lattice/types/generated\";\nimport { filterOptions, stringProp } from \"../filter-values\";\nimport { fieldClass } from \"./filter-value-input\";\n\nexport type DateRangeValue = { from?: string; until?: string };\n\nexport type FilterOptionSearch = (query: string) => Promise<Option[]>;\n\n/**\n * Renders the control for a single dedicated table filter, dispatching on the\n * filter's `type`. The current value comes from the table state; `onChange`\n * pushes a new value (an empty value clears the filter).\n */\nexport function TableFilterControl({\n filter,\n value,\n processing,\n onChange,\n onSearch,\n}: {\n filter: FilterData;\n value: unknown;\n processing: boolean;\n onChange: (value: unknown) => void;\n onSearch?: FilterOptionSearch;\n}) {\n switch (filter.type) {\n case \"select\":\n if (filter.props.searchable === true && onSearch) {\n return (\n <SearchableSelectControl\n filter={filter}\n value={value}\n processing={processing}\n onChange={onChange}\n onSearch={onSearch}\n />\n );\n }\n\n return filter.props.multiple === true ? (\n <MultiSelectControl\n filter={filter}\n value={value}\n processing={processing}\n onChange={onChange}\n />\n ) : (\n <SelectControl filter={filter} value={value} processing={processing} onChange={onChange} />\n );\n case \"ternary\":\n return (\n <TernaryControl filter={filter} value={value} processing={processing} onChange={onChange} />\n );\n case \"date-range\":\n return (\n <DateRangeControl\n filter={filter}\n value={value}\n processing={processing}\n onChange={onChange}\n />\n );\n case \"toggle\":\n return (\n <ToggleControl filter={filter} value={value} processing={processing} onChange={onChange} />\n );\n default:\n return null;\n }\n}\n\nfunction SelectControl({\n filter,\n value,\n processing,\n onChange,\n}: {\n filter: FilterData;\n value: unknown;\n processing: boolean;\n onChange: (value: unknown) => void;\n}) {\n const { t } = useT(\"lattice\");\n const placeholder = stringProp(filter, \"placeholder\", t(\"table.filter.all\", \"All\"));\n\n return (\n <select\n aria-label={filter.label}\n data-test={`table-filter-${filter.key}`}\n className={fieldClass}\n disabled={processing}\n value={typeof value === \"string\" ? value : \"\"}\n onChange={(event) => onChange(event.target.value)}\n >\n <option value=\"\">{placeholder}</option>\n {filterOptions(filter).map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n );\n}\n\nfunction MultiSelectControl({\n filter,\n value,\n processing,\n onChange,\n}: {\n filter: FilterData;\n value: unknown;\n processing: boolean;\n onChange: (value: unknown) => void;\n}) {\n const { t } = useT(\"lattice\");\n const [open, setOpen] = useState(false);\n const selected = Array.isArray(value) ? (value as string[]) : [];\n\n function toggle(optionValue: string): void {\n const next = selected.includes(optionValue)\n ? selected.filter((item) => item !== optionValue)\n : [...selected, optionValue];\n\n onChange(next);\n }\n\n const summary =\n selected.length === 0\n ? stringProp(filter, \"placeholder\", t(\"table.filter.all\", \"All\"))\n : t(\"table.filter.selectedCount\", \"{{amount}} selected\", { amount: selected.length });\n\n return (\n <div className=\"relative\">\n <button\n type=\"button\"\n aria-label={filter.label}\n aria-expanded={open}\n data-test={`table-filter-${filter.key}`}\n className={`${fieldClass} flex items-center justify-between gap-2`}\n disabled={processing}\n onClick={() => setOpen((current) => !current)}\n >\n <span className=\"truncate\">{summary}</span>\n <Icon name=\"chevron-down\" aria-hidden=\"true\" className=\"size-lt-icon-sm shrink-0\" />\n </button>\n {open && (\n <div className=\"absolute z-10 mt-1 min-w-full rounded-lt-sm border border-lt-border bg-lt-bg p-1 shadow-lt-md\">\n {filterOptions(filter).map((option) => (\n <label\n key={option.value}\n className=\"flex cursor-pointer items-center gap-2 rounded-lt-sm px-2 py-1.5 text-sm hover:bg-lt-muted\"\n >\n <Checkbox\n aria-label={option.label}\n data-test={`table-filter-${filter.key}-${option.value}`}\n checked={selected.includes(option.value)}\n disabled={processing}\n onCheckedChange={() => toggle(option.value)}\n />\n <span className=\"truncate\">{option.label}</span>\n </label>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nfunction SearchableSelectControl({\n filter,\n value,\n processing,\n onChange,\n onSearch,\n}: {\n filter: FilterData;\n value: unknown;\n processing: boolean;\n onChange: (value: unknown) => void;\n onSearch: FilterOptionSearch;\n}) {\n const { t } = useT(\"lattice\");\n const multiple = filter.props.multiple === true;\n const [open, setOpen] = useState(false);\n const [results, setResults] = useState<Option[]>(() => filterOptions(filter));\n const [loading, setLoading] = useState(false);\n\n const selected = multiple\n ? Array.isArray(value)\n ? (value as string[])\n : []\n : typeof value === \"string\" && value !== \"\"\n ? [value]\n : [];\n\n const labels = new Map(\n [...filterOptions(filter), ...results].map((option) => [option.value, option.label]),\n );\n const summary =\n selected.length === 0\n ? stringProp(filter, \"placeholder\", t(\"table.filter.all\", \"All\"))\n : multiple\n ? t(\"table.filter.selectedCount\", \"{{amount}} selected\", { amount: selected.length })\n : (labels.get(selected[0]) ?? selected[0]);\n\n function search(query: string): void {\n setLoading(true);\n void onSearch(query).then((options) => {\n setResults(options);\n setLoading(false);\n });\n }\n\n function choose(optionValue: string): void {\n onChange(\n multiple\n ? selected.includes(optionValue)\n ? selected.filter((item) => item !== optionValue)\n : [...selected, optionValue]\n : optionValue,\n );\n }\n\n return (\n <Combobox\n contentClassName=\"w-60\"\n loading={loading}\n multiple={multiple}\n onSearch={search}\n onSelect={choose}\n open={open}\n onOpenChange={setOpen}\n options={results}\n searchLabel={t(\"table.filter.search\", \"Search\")}\n selected={selected}\n testId={`table-filter-${filter.key}`}\n trigger={\n <>\n <span className=\"truncate\">{summary}</span>\n <Icon name=\"chevron-down\" aria-hidden=\"true\" className=\"size-lt-icon-sm shrink-0\" />\n </>\n }\n triggerClassName={cn(fieldClass, \"flex items-center justify-between gap-2\")}\n triggerProps={{\n \"aria-label\": filter.label,\n \"data-test\": `table-filter-${filter.key}`,\n disabled: processing,\n }}\n />\n );\n}\n\nfunction TernaryControl({\n filter,\n value,\n processing,\n onChange,\n}: {\n filter: FilterData;\n value: unknown;\n processing: boolean;\n onChange: (value: unknown) => void;\n}) {\n const { t } = useT(\"lattice\");\n const placeholder = stringProp(filter, \"placeholder\", t(\"table.filter.all\", \"All\"));\n const trueLabel = stringProp(filter, \"trueLabel\", t(\"table.filter.true\", \"True\"));\n const falseLabel = stringProp(filter, \"falseLabel\", t(\"table.filter.false\", \"False\"));\n\n return (\n <select\n aria-label={filter.label}\n data-test={`table-filter-${filter.key}`}\n className={fieldClass}\n disabled={processing}\n value={typeof value === \"string\" ? value : \"\"}\n onChange={(event) => onChange(event.target.value)}\n >\n <option value=\"\">{placeholder}</option>\n <option value=\"true\">{trueLabel}</option>\n <option value=\"false\">{falseLabel}</option>\n </select>\n );\n}\n\nfunction DateRangeControl({\n filter,\n value,\n processing,\n onChange,\n}: {\n filter: FilterData;\n value: unknown;\n processing: boolean;\n onChange: (value: unknown) => void;\n}) {\n const { t } = useT(\"lattice\");\n const range: DateRangeValue = value && typeof value === \"object\" ? (value as DateRangeValue) : {};\n\n function commit(next: DateRangeValue): void {\n onChange({ from: next.from ?? \"\", until: next.until ?? \"\" });\n }\n\n return (\n <div className=\"flex items-center gap-1\">\n <input\n type=\"date\"\n aria-label={t(\"table.filter.from\", \"{{label}} from\", { label: filter.label })}\n data-test={`table-filter-${filter.key}-from`}\n className={fieldClass}\n disabled={processing}\n value={range.from ?? \"\"}\n onChange={(event) => commit({ ...range, from: event.target.value })}\n />\n <span className=\"text-lt-muted-fg\" aria-hidden=\"true\">\n –\n </span>\n <input\n type=\"date\"\n aria-label={t(\"table.filter.until\", \"{{label}} until\", { label: filter.label })}\n data-test={`table-filter-${filter.key}-until`}\n className={fieldClass}\n disabled={processing}\n value={range.until ?? \"\"}\n onChange={(event) => commit({ ...range, until: event.target.value })}\n />\n </div>\n );\n}\n\nfunction ToggleControl({\n filter,\n value,\n processing,\n onChange,\n}: {\n filter: FilterData;\n value: unknown;\n processing: boolean;\n onChange: (value: unknown) => void;\n}) {\n const checked = value === \"1\" || value === true || value === \"true\";\n\n return (\n <label className=\"flex h-9 cursor-pointer items-center gap-2 text-sm\">\n <Checkbox\n aria-label={filter.label}\n data-test={`table-filter-${filter.key}`}\n checked={checked}\n disabled={processing}\n onCheckedChange={(next) => onChange(next === true ? \"1\" : \"\")}\n />\n <span>{filter.label}</span>\n </label>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"filter-controls.js","names":[],"sources":["../../../resources/js/table/components/filter-controls.tsx"],"sourcesContent":["import { Icon } from \"@lattice-php/lattice/icons\";\nimport { useState } from \"react\";\nimport { Checkbox } from \"@lattice-php/lattice/core/components/checkbox\";\nimport { Combobox } from \"@lattice-php/lattice/core/components/combobox\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport type { FilterData, Option } from \"@lattice-php/lattice/types/generated\";\nimport { filterOptions, stringProp } from \"../filter-values\";\nimport { fieldClass } from \"./filter-value-input\";\n\nexport type DateRangeValue = { from?: string; until?: string };\n\nexport type FilterOptionSearch = (query: string) => Promise<Option[]>;\n\n/**\n * Renders the control for a single dedicated table filter, dispatching on the\n * filter's `type`. The current value comes from the table state; `onChange`\n * pushes a new value (an empty value clears the filter).\n */\nexport function TableFilterControl({\n filter,\n value,\n processing,\n onChange,\n onSearch,\n}: {\n filter: FilterData;\n value: unknown;\n processing: boolean;\n onChange: (value: unknown) => void;\n onSearch?: FilterOptionSearch;\n}) {\n switch (filter.type) {\n case \"select\":\n if (filter.props.searchable === true && onSearch) {\n return (\n <SearchableSelectControl\n filter={filter}\n value={value}\n processing={processing}\n onChange={onChange}\n onSearch={onSearch}\n />\n );\n }\n\n return filter.props.multiple === true ? (\n <MultiSelectControl\n filter={filter}\n value={value}\n processing={processing}\n onChange={onChange}\n />\n ) : (\n <SelectControl filter={filter} value={value} processing={processing} onChange={onChange} />\n );\n case \"ternary\":\n return (\n <TernaryControl filter={filter} value={value} processing={processing} onChange={onChange} />\n );\n case \"date-range\":\n return (\n <DateRangeControl\n filter={filter}\n value={value}\n processing={processing}\n onChange={onChange}\n />\n );\n case \"toggle\":\n return (\n <ToggleControl filter={filter} value={value} processing={processing} onChange={onChange} />\n );\n default:\n return null;\n }\n}\n\nfunction SelectControl({\n filter,\n value,\n processing,\n onChange,\n}: {\n filter: FilterData;\n value: unknown;\n processing: boolean;\n onChange: (value: unknown) => void;\n}) {\n const { t } = useT(\"lattice\");\n const placeholder = stringProp(filter, \"placeholder\", t(\"table.filter.all\", \"All\"));\n\n return (\n <select\n aria-label={filter.label}\n data-test={`table-filter-${filter.key}`}\n className={fieldClass}\n disabled={processing}\n value={typeof value === \"string\" ? value : \"\"}\n onChange={(event) => onChange(event.target.value)}\n >\n <option value=\"\">{placeholder}</option>\n {filterOptions(filter).map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n );\n}\n\nfunction MultiSelectControl({\n filter,\n value,\n processing,\n onChange,\n}: {\n filter: FilterData;\n value: unknown;\n processing: boolean;\n onChange: (value: unknown) => void;\n}) {\n const { t } = useT(\"lattice\");\n const [open, setOpen] = useState(false);\n const selected = Array.isArray(value) ? (value as string[]) : [];\n\n function toggle(optionValue: string): void {\n const next = selected.includes(optionValue)\n ? selected.filter((item) => item !== optionValue)\n : [...selected, optionValue];\n\n onChange(next);\n }\n\n const summary =\n selected.length === 0\n ? stringProp(filter, \"placeholder\", t(\"table.filter.all\", \"All\"))\n : t(\"table.filter.selectedCount\", \"{{amount}} selected\", { amount: selected.length });\n\n return (\n <div className=\"relative\">\n <button\n type=\"button\"\n aria-label={filter.label}\n aria-expanded={open}\n data-test={`table-filter-${filter.key}`}\n className={`${fieldClass} flex items-center justify-between gap-2`}\n disabled={processing}\n onClick={() => setOpen((current) => !current)}\n >\n <span className=\"truncate\">{summary}</span>\n <Icon name=\"chevron-down\" aria-hidden=\"true\" className=\"size-lt-icon-sm shrink-0\" />\n </button>\n {open && (\n <div className=\"absolute z-lt-dropdown mt-1 min-w-full rounded-lt-sm border border-lt-border bg-lt-bg p-1 shadow-lt-md\">\n {filterOptions(filter).map((option) => (\n <label\n key={option.value}\n className=\"flex cursor-pointer items-center gap-2 rounded-lt-sm px-2 py-1.5 text-sm hover:bg-lt-muted\"\n >\n <Checkbox\n aria-label={option.label}\n data-test={`table-filter-${filter.key}-${option.value}`}\n checked={selected.includes(option.value)}\n disabled={processing}\n onCheckedChange={() => toggle(option.value)}\n />\n <span className=\"truncate\">{option.label}</span>\n </label>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nfunction SearchableSelectControl({\n filter,\n value,\n processing,\n onChange,\n onSearch,\n}: {\n filter: FilterData;\n value: unknown;\n processing: boolean;\n onChange: (value: unknown) => void;\n onSearch: FilterOptionSearch;\n}) {\n const { t } = useT(\"lattice\");\n const multiple = filter.props.multiple === true;\n const [open, setOpen] = useState(false);\n const [results, setResults] = useState<Option[]>(() => filterOptions(filter));\n const [loading, setLoading] = useState(false);\n\n const selected = multiple\n ? Array.isArray(value)\n ? (value as string[])\n : []\n : typeof value === \"string\" && value !== \"\"\n ? [value]\n : [];\n\n const labels = new Map(\n [...filterOptions(filter), ...results].map((option) => [option.value, option.label]),\n );\n const summary =\n selected.length === 0\n ? stringProp(filter, \"placeholder\", t(\"table.filter.all\", \"All\"))\n : multiple\n ? t(\"table.filter.selectedCount\", \"{{amount}} selected\", { amount: selected.length })\n : (labels.get(selected[0]) ?? selected[0]);\n\n function search(query: string): void {\n setLoading(true);\n void onSearch(query).then((options) => {\n setResults(options);\n setLoading(false);\n });\n }\n\n function choose(optionValue: string): void {\n onChange(\n multiple\n ? selected.includes(optionValue)\n ? selected.filter((item) => item !== optionValue)\n : [...selected, optionValue]\n : optionValue,\n );\n }\n\n return (\n <Combobox\n contentClassName=\"w-60\"\n loading={loading}\n multiple={multiple}\n onSearch={search}\n onSelect={choose}\n open={open}\n onOpenChange={setOpen}\n options={results}\n searchLabel={t(\"table.filter.search\", \"Search\")}\n selected={selected}\n testId={`table-filter-${filter.key}`}\n trigger={\n <>\n <span className=\"truncate\">{summary}</span>\n <Icon name=\"chevron-down\" aria-hidden=\"true\" className=\"size-lt-icon-sm shrink-0\" />\n </>\n }\n triggerClassName={cn(fieldClass, \"flex items-center justify-between gap-2\")}\n triggerProps={{\n \"aria-label\": filter.label,\n \"data-test\": `table-filter-${filter.key}`,\n disabled: processing,\n }}\n />\n );\n}\n\nfunction TernaryControl({\n filter,\n value,\n processing,\n onChange,\n}: {\n filter: FilterData;\n value: unknown;\n processing: boolean;\n onChange: (value: unknown) => void;\n}) {\n const { t } = useT(\"lattice\");\n const placeholder = stringProp(filter, \"placeholder\", t(\"table.filter.all\", \"All\"));\n const trueLabel = stringProp(filter, \"trueLabel\", t(\"table.filter.true\", \"True\"));\n const falseLabel = stringProp(filter, \"falseLabel\", t(\"table.filter.false\", \"False\"));\n\n return (\n <select\n aria-label={filter.label}\n data-test={`table-filter-${filter.key}`}\n className={fieldClass}\n disabled={processing}\n value={typeof value === \"string\" ? value : \"\"}\n onChange={(event) => onChange(event.target.value)}\n >\n <option value=\"\">{placeholder}</option>\n <option value=\"true\">{trueLabel}</option>\n <option value=\"false\">{falseLabel}</option>\n </select>\n );\n}\n\nfunction DateRangeControl({\n filter,\n value,\n processing,\n onChange,\n}: {\n filter: FilterData;\n value: unknown;\n processing: boolean;\n onChange: (value: unknown) => void;\n}) {\n const { t } = useT(\"lattice\");\n const range: DateRangeValue = value && typeof value === \"object\" ? (value as DateRangeValue) : {};\n\n function commit(next: DateRangeValue): void {\n onChange({ from: next.from ?? \"\", until: next.until ?? \"\" });\n }\n\n return (\n <div className=\"flex items-center gap-1\">\n <input\n type=\"date\"\n aria-label={t(\"table.filter.from\", \"{{label}} from\", { label: filter.label })}\n data-test={`table-filter-${filter.key}-from`}\n className={fieldClass}\n disabled={processing}\n value={range.from ?? \"\"}\n onChange={(event) => commit({ ...range, from: event.target.value })}\n />\n <span className=\"text-lt-muted-fg\" aria-hidden=\"true\">\n –\n </span>\n <input\n type=\"date\"\n aria-label={t(\"table.filter.until\", \"{{label}} until\", { label: filter.label })}\n data-test={`table-filter-${filter.key}-until`}\n className={fieldClass}\n disabled={processing}\n value={range.until ?? \"\"}\n onChange={(event) => commit({ ...range, until: event.target.value })}\n />\n </div>\n );\n}\n\nfunction ToggleControl({\n filter,\n value,\n processing,\n onChange,\n}: {\n filter: FilterData;\n value: unknown;\n processing: boolean;\n onChange: (value: unknown) => void;\n}) {\n const checked = value === \"1\" || value === true || value === \"true\";\n\n return (\n <label className=\"flex h-lt-control-md cursor-pointer items-center gap-2 text-sm\">\n <Checkbox\n aria-label={filter.label}\n data-test={`table-filter-${filter.key}`}\n checked={checked}\n disabled={processing}\n onCheckedChange={(next) => onChange(next === true ? \"1\" : \"\")}\n />\n <span>{filter.label}</span>\n </label>\n );\n}\n"],"mappings":"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"}
|
|
@@ -4,6 +4,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
4
4
|
function TablePagination({ pagination, currentPage, processing, mode, hasNextPage, visiblePages, infiniteLoaderRef, onPage, onLoadMore }) {
|
|
5
5
|
const { t } = useT("lattice");
|
|
6
6
|
return /* @__PURE__ */ jsxs("div", {
|
|
7
|
+
"data-slot": "table-pagination",
|
|
7
8
|
className: "flex items-center justify-between gap-3 border-t border-lt-border p-4 text-sm",
|
|
8
9
|
children: [/* @__PURE__ */ jsx("span", { children: pagination.total == null ? t("table.pagination.page", "Page {{page}}", { page: currentPage }) : t("table.pagination.showing", "Showing {{from}}-{{to}} of {{total}}", {
|
|
9
10
|
from: pagination.from ?? 0,
|
|
@@ -15,7 +16,7 @@ function TablePagination({ pagination, currentPage, processing, mode, hasNextPag
|
|
|
15
16
|
children: pagination.hasMore ? /* @__PURE__ */ jsx("button", {
|
|
16
17
|
type: "button",
|
|
17
18
|
"data-test": "pagination-load-more",
|
|
18
|
-
className: "h-
|
|
19
|
+
className: "h-lt-control-md rounded-lt-sm border border-lt-border px-3 font-medium disabled:opacity-50",
|
|
19
20
|
disabled: processing,
|
|
20
21
|
onClick: onLoadMore,
|
|
21
22
|
children: processing ? t("table.pagination.loading", "Loading...") : t("table.pagination.loadMore", "Load more")
|
|
@@ -28,14 +29,14 @@ function TablePagination({ pagination, currentPage, processing, mode, hasNextPag
|
|
|
28
29
|
children: [/* @__PURE__ */ jsx("button", {
|
|
29
30
|
type: "button",
|
|
30
31
|
"data-test": "pagination-previous",
|
|
31
|
-
className: "h-
|
|
32
|
+
className: "h-lt-control-md rounded-lt-sm border border-lt-border px-3 font-medium disabled:opacity-50",
|
|
32
33
|
disabled: processing || currentPage <= 1,
|
|
33
34
|
onClick: () => onPage(currentPage - 1),
|
|
34
35
|
children: t("table.pagination.previous", "Previous")
|
|
35
36
|
}), /* @__PURE__ */ jsx("button", {
|
|
36
37
|
type: "button",
|
|
37
38
|
"data-test": "pagination-next",
|
|
38
|
-
className: "h-
|
|
39
|
+
className: "h-lt-control-md rounded-lt-sm border border-lt-border px-3 font-medium disabled:opacity-50",
|
|
39
40
|
disabled: processing || !hasNextPage,
|
|
40
41
|
onClick: () => onPage(currentPage + 1),
|
|
41
42
|
children: t("table.pagination.next", "Next")
|
|
@@ -46,7 +47,7 @@ function TablePagination({ pagination, currentPage, processing, mode, hasNextPag
|
|
|
46
47
|
/* @__PURE__ */ jsx("button", {
|
|
47
48
|
type: "button",
|
|
48
49
|
"data-test": "pagination-previous",
|
|
49
|
-
className: "h-
|
|
50
|
+
className: "h-lt-control-md rounded-lt-sm border border-lt-border px-3 font-medium disabled:opacity-50",
|
|
50
51
|
disabled: processing || currentPage <= 1,
|
|
51
52
|
onClick: () => onPage(currentPage - 1),
|
|
52
53
|
children: t("table.pagination.previous", "Previous")
|
|
@@ -54,7 +55,7 @@ function TablePagination({ pagination, currentPage, processing, mode, hasNextPag
|
|
|
54
55
|
visiblePages.map((pageNumber) => /* @__PURE__ */ jsx("button", {
|
|
55
56
|
type: "button",
|
|
56
57
|
"data-test": `pagination-page-${pageNumber}`,
|
|
57
|
-
className: "inline-flex size-
|
|
58
|
+
className: "inline-flex size-lt-control-md items-center justify-center rounded-lt-sm border border-lt-border font-medium disabled:opacity-50",
|
|
58
59
|
disabled: processing || pageNumber === currentPage,
|
|
59
60
|
"aria-current": pageNumber === currentPage ? "page" : void 0,
|
|
60
61
|
"aria-label": t("table.pagination.page", "Page {{page}}", { page: pageNumber }),
|
|
@@ -64,7 +65,7 @@ function TablePagination({ pagination, currentPage, processing, mode, hasNextPag
|
|
|
64
65
|
/* @__PURE__ */ jsx("button", {
|
|
65
66
|
type: "button",
|
|
66
67
|
"data-test": "pagination-next",
|
|
67
|
-
className: "h-
|
|
68
|
+
className: "h-lt-control-md rounded-lt-sm border border-lt-border px-3 font-medium disabled:opacity-50",
|
|
68
69
|
disabled: processing || !hasNextPage,
|
|
69
70
|
onClick: () => onPage(currentPage + 1),
|
|
70
71
|
children: t("table.pagination.next", "Next")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pagination.js","names":[],"sources":["../../../resources/js/table/components/pagination.tsx"],"sourcesContent":["import type { RefObject } from \"react\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport type { PaginationType } from \"@lattice-php/lattice/types/generated\";\nimport type { TablePagination as TablePaginationData } from \"../types\";\n\nexport function TablePagination({\n pagination,\n currentPage,\n processing,\n mode,\n hasNextPage,\n visiblePages,\n infiniteLoaderRef,\n onPage,\n onLoadMore,\n}: {\n pagination: TablePaginationData;\n currentPage: number;\n processing: boolean;\n mode: PaginationType;\n hasNextPage: boolean;\n visiblePages: number[];\n infiniteLoaderRef: RefObject<HTMLDivElement | null>;\n onPage: (page: number) => void;\n onLoadMore: () => void;\n}) {\n const { t } = useT(\"lattice\");\n\n return (\n <div
|
|
1
|
+
{"version":3,"file":"pagination.js","names":[],"sources":["../../../resources/js/table/components/pagination.tsx"],"sourcesContent":["import type { RefObject } from \"react\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport type { PaginationType } from \"@lattice-php/lattice/types/generated\";\nimport type { TablePagination as TablePaginationData } from \"../types\";\n\nexport function TablePagination({\n pagination,\n currentPage,\n processing,\n mode,\n hasNextPage,\n visiblePages,\n infiniteLoaderRef,\n onPage,\n onLoadMore,\n}: {\n pagination: TablePaginationData;\n currentPage: number;\n processing: boolean;\n mode: PaginationType;\n hasNextPage: boolean;\n visiblePages: number[];\n infiniteLoaderRef: RefObject<HTMLDivElement | null>;\n onPage: (page: number) => void;\n onLoadMore: () => void;\n}) {\n const { t } = useT(\"lattice\");\n\n return (\n <div\n data-slot=\"table-pagination\"\n className=\"flex items-center justify-between gap-3 border-t border-lt-border p-4 text-sm\"\n >\n <span>\n {pagination.total == null\n ? t(\"table.pagination.page\", \"Page {{page}}\", { page: currentPage })\n : t(\"table.pagination.showing\", \"Showing {{from}}-{{to}} of {{total}}\", {\n from: pagination.from ?? 0,\n to: pagination.to ?? 0,\n total: pagination.total,\n })}\n </span>\n {mode === \"infinite\" ? (\n <div ref={infiniteLoaderRef} className=\"flex items-center gap-2\">\n {pagination.hasMore ? (\n <button\n type=\"button\"\n data-test=\"pagination-load-more\"\n className=\"h-lt-control-md rounded-lt-sm border border-lt-border px-3 font-medium disabled:opacity-50\"\n disabled={processing}\n onClick={onLoadMore}\n >\n {processing\n ? t(\"table.pagination.loading\", \"Loading...\")\n : t(\"table.pagination.loadMore\", \"Load more\")}\n </button>\n ) : (\n <span className=\"text-lt-muted-fg\">\n {t(\"table.pagination.allLoaded\", \"All rows loaded\")}\n </span>\n )}\n </div>\n ) : mode === \"simple\" ? (\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n data-test=\"pagination-previous\"\n className=\"h-lt-control-md rounded-lt-sm border border-lt-border px-3 font-medium disabled:opacity-50\"\n disabled={processing || currentPage <= 1}\n onClick={() => onPage(currentPage - 1)}\n >\n {t(\"table.pagination.previous\", \"Previous\")}\n </button>\n <button\n type=\"button\"\n data-test=\"pagination-next\"\n className=\"h-lt-control-md rounded-lt-sm border border-lt-border px-3 font-medium disabled:opacity-50\"\n disabled={processing || !hasNextPage}\n onClick={() => onPage(currentPage + 1)}\n >\n {t(\"table.pagination.next\", \"Next\")}\n </button>\n </div>\n ) : mode === \"table\" ? (\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n data-test=\"pagination-previous\"\n className=\"h-lt-control-md rounded-lt-sm border border-lt-border px-3 font-medium disabled:opacity-50\"\n disabled={processing || currentPage <= 1}\n onClick={() => onPage(currentPage - 1)}\n >\n {t(\"table.pagination.previous\", \"Previous\")}\n </button>\n {visiblePages.map((pageNumber) => (\n <button\n key={pageNumber}\n type=\"button\"\n data-test={`pagination-page-${pageNumber}`}\n className=\"inline-flex size-lt-control-md items-center justify-center rounded-lt-sm border border-lt-border font-medium disabled:opacity-50\"\n disabled={processing || pageNumber === currentPage}\n aria-current={pageNumber === currentPage ? \"page\" : undefined}\n aria-label={t(\"table.pagination.page\", \"Page {{page}}\", { page: pageNumber })}\n onClick={() => onPage(pageNumber)}\n >\n {pageNumber}\n </button>\n ))}\n <button\n type=\"button\"\n data-test=\"pagination-next\"\n className=\"h-lt-control-md rounded-lt-sm border border-lt-border px-3 font-medium disabled:opacity-50\"\n disabled={processing || !hasNextPage}\n onClick={() => onPage(currentPage + 1)}\n >\n {t(\"table.pagination.next\", \"Next\")}\n </button>\n </div>\n ) : null}\n </div>\n );\n}\n"],"mappings":";;;AAKA,SAAgB,gBAAgB,EAC9B,YACA,aACA,YACA,MACA,aACA,cACA,mBACA,QACA,cAWC;CACD,MAAM,EAAE,MAAM,KAAK,SAAS;CAE5B,OACE,qBAAC,OAAD;EACE,aAAU;EACV,WAAU;YAFZ,CAIE,oBAAC,QAAD,EAAA,UACG,WAAW,SAAS,OACjB,EAAE,yBAAyB,iBAAiB,EAAE,MAAM,YAAY,CAAC,IACjE,EAAE,4BAA4B,wCAAwC;GACpE,MAAM,WAAW,QAAQ;GACzB,IAAI,WAAW,MAAM;GACrB,OAAO,WAAW;EACpB,CAAC,EACD,CAAA,GACL,SAAS,aACR,oBAAC,OAAD;GAAK,KAAK;GAAmB,WAAU;aACpC,WAAW,UACV,oBAAC,UAAD;IACE,MAAK;IACL,aAAU;IACV,WAAU;IACV,UAAU;IACV,SAAS;cAER,aACG,EAAE,4BAA4B,YAAY,IAC1C,EAAE,6BAA6B,WAAW;GACxC,CAAA,IAER,oBAAC,QAAD;IAAM,WAAU;cACb,EAAE,8BAA8B,iBAAiB;GAC9C,CAAA;EAEL,CAAA,IACH,SAAS,WACX,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,UAAD;IACE,MAAK;IACL,aAAU;IACV,WAAU;IACV,UAAU,cAAc,eAAe;IACvC,eAAe,OAAO,cAAc,CAAC;cAEpC,EAAE,6BAA6B,UAAU;GACpC,CAAA,GACR,oBAAC,UAAD;IACE,MAAK;IACL,aAAU;IACV,WAAU;IACV,UAAU,cAAc,CAAC;IACzB,eAAe,OAAO,cAAc,CAAC;cAEpC,EAAE,yBAAyB,MAAM;GAC5B,CAAA,CACL;OACH,SAAS,UACX,qBAAC,OAAD;GAAK,WAAU;aAAf;IACE,oBAAC,UAAD;KACE,MAAK;KACL,aAAU;KACV,WAAU;KACV,UAAU,cAAc,eAAe;KACvC,eAAe,OAAO,cAAc,CAAC;eAEpC,EAAE,6BAA6B,UAAU;IACpC,CAAA;IACP,aAAa,KAAK,eACjB,oBAAC,UAAD;KAEE,MAAK;KACL,aAAW,mBAAmB;KAC9B,WAAU;KACV,UAAU,cAAc,eAAe;KACvC,gBAAc,eAAe,cAAc,SAAS,KAAA;KACpD,cAAY,EAAE,yBAAyB,iBAAiB,EAAE,MAAM,WAAW,CAAC;KAC5E,eAAe,OAAO,UAAU;eAE/B;IACK,GAVD,UAUC,CACT;IACD,oBAAC,UAAD;KACE,MAAK;KACL,aAAU;KACV,WAAU;KACV,UAAU,cAAc,CAAC;KACzB,eAAe,OAAO,cAAc,CAAC;eAEpC,EAAE,yBAAyB,MAAM;IAC5B,CAAA;GACL;OACH,IACD;;AAET"}
|
|
@@ -70,6 +70,7 @@ var TableComponent = ({ node }) => {
|
|
|
70
70
|
trailingTracks: utilityTracks.trailingTracks
|
|
71
71
|
});
|
|
72
72
|
return /* @__PURE__ */ jsxs("div", {
|
|
73
|
+
"data-slot": "table",
|
|
73
74
|
"data-lattice-component": node.id,
|
|
74
75
|
className: "relative",
|
|
75
76
|
children: [hasOverrides && /* @__PURE__ */ jsx("button", {
|
|
@@ -84,6 +85,7 @@ var TableComponent = ({ node }) => {
|
|
|
84
85
|
className: "size-lt-icon-sm"
|
|
85
86
|
})
|
|
86
87
|
}), /* @__PURE__ */ jsxs("div", {
|
|
88
|
+
"data-slot": "table-scroll",
|
|
87
89
|
className: "overflow-x-auto rounded-lt-sm border border-lt-border",
|
|
88
90
|
children: [
|
|
89
91
|
hasDedicatedFilters && hasActiveFilters && /* @__PURE__ */ jsx(FilterBar, {
|
|
@@ -120,6 +122,7 @@ var TableComponent = ({ node }) => {
|
|
|
120
122
|
className: "min-w-full text-base",
|
|
121
123
|
role: "table",
|
|
122
124
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
125
|
+
"data-slot": "table-header",
|
|
123
126
|
className: "border-b border-lt-border bg-lt-muted/50",
|
|
124
127
|
role: "rowgroup",
|
|
125
128
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
@@ -210,6 +213,7 @@ var TableComponent = ({ node }) => {
|
|
|
210
213
|
children: t("table.loading", "Loading rows...")
|
|
211
214
|
})
|
|
212
215
|
}) : rowEntries.length === 0 ? /* @__PURE__ */ jsx("div", {
|
|
216
|
+
"data-slot": "table-empty",
|
|
213
217
|
className: "p-8 text-center text-lt-muted-fg",
|
|
214
218
|
role: "row",
|
|
215
219
|
children: /* @__PURE__ */ jsx("div", {
|
|
@@ -218,12 +222,13 @@ var TableComponent = ({ node }) => {
|
|
|
218
222
|
})
|
|
219
223
|
}) : rowEntries.map(({ row, actions, key }) => {
|
|
220
224
|
return /* @__PURE__ */ jsxs("div", {
|
|
225
|
+
"data-slot": "table-row",
|
|
221
226
|
className: `grid grid-cols-1 border-b border-lt-border last:border-b-0 md:grid-cols-[var(--lattice-table-columns)] ${striped ? "odd:bg-lt-muted/30" : ""}`,
|
|
222
227
|
role: "row",
|
|
223
228
|
style: { "--lattice-table-columns": gridTemplateColumns },
|
|
224
229
|
children: [
|
|
225
230
|
hasBulkActions && /* @__PURE__ */ jsx("div", {
|
|
226
|
-
className: "flex items-center
|
|
231
|
+
className: "flex items-center px-lt-cell-x py-lt-cell-y",
|
|
227
232
|
role: "cell",
|
|
228
233
|
children: /* @__PURE__ */ jsx(Checkbox, {
|
|
229
234
|
"aria-label": t("table.selectRow", "Select row {{key}}", { key }),
|
|
@@ -233,7 +238,8 @@ var TableComponent = ({ node }) => {
|
|
|
233
238
|
})
|
|
234
239
|
}),
|
|
235
240
|
visibleColumns.map((column) => /* @__PURE__ */ jsxs("div", {
|
|
236
|
-
|
|
241
|
+
"data-slot": "table-cell",
|
|
242
|
+
className: cn("grid min-w-0 gap-1 overflow-hidden px-lt-cell-x py-lt-cell-y align-middle", alignText(column.align), alignJustifyItems(column.align)),
|
|
237
243
|
role: "cell",
|
|
238
244
|
children: [/* @__PURE__ */ jsx("span", {
|
|
239
245
|
"aria-hidden": "true",
|
|
@@ -249,7 +255,7 @@ var TableComponent = ({ node }) => {
|
|
|
249
255
|
})]
|
|
250
256
|
}, column.key)),
|
|
251
257
|
hasTrailingUtility && /* @__PURE__ */ jsx("div", {
|
|
252
|
-
className: cn("items-center justify-start gap-2
|
|
258
|
+
className: cn("items-center justify-start gap-2 px-lt-cell-x py-lt-cell-y md:justify-end", actions.length > 0 ? "flex" : "hidden md:flex"),
|
|
253
259
|
role: "cell",
|
|
254
260
|
children: actions.map((action, actionIndex) => /* @__PURE__ */ jsx(TableActionNode, { node: action }, action.key ?? action.id ?? actionIndex))
|
|
255
261
|
})
|