@lattice-php/lattice 0.9.0 → 0.11.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-form.js +1 -1
- package/dist/action/components/action-form.js.map +1 -1
- package/dist/chat/components/chat-box.js +1 -1
- package/dist/chat/components/chat-box.js.map +1 -1
- package/dist/chat/components/prompt-input.js +2 -2
- package/dist/chat/components/prompt-input.js.map +1 -1
- package/dist/core/components/combobox.js +1 -1
- package/dist/core/components/combobox.js.map +1 -1
- package/dist/core/components/fragment.js +4 -1
- package/dist/core/components/fragment.js.map +1 -1
- package/dist/core/components/icon.js +4 -1
- package/dist/core/components/icon.js.map +1 -1
- package/dist/core/components/info-tooltip.js +1 -1
- package/dist/core/components/info-tooltip.js.map +1 -1
- package/dist/core/components/spinner.js +1 -1
- package/dist/core/components/spinner.js.map +1 -1
- package/dist/core/components/tabs.js +1 -1
- package/dist/core/components/tabs.js.map +1 -1
- package/dist/core/components/text.js +4 -1
- package/dist/core/components/text.js.map +1 -1
- package/dist/form/components/fields/date-input.js +7 -6
- package/dist/form/components/fields/date-input.js.map +1 -1
- package/dist/form/components/fields/date-picker-control.d.ts +18 -0
- package/dist/form/components/fields/date-picker-control.js +199 -0
- package/dist/form/components/fields/date-picker-control.js.map +1 -0
- package/dist/form/components/fields/date-picker-value.d.ts +10 -0
- package/dist/form/components/fields/date-picker-value.js +125 -0
- package/dist/form/components/fields/date-picker-value.js.map +1 -0
- package/dist/form/components/fields/date-time-input.d.ts +2 -0
- package/dist/form/components/fields/date-time-input.js +34 -0
- package/dist/form/components/fields/date-time-input.js.map +1 -0
- package/dist/form/components/fields/file-upload.js +3 -3
- package/dist/form/components/fields/file-upload.js.map +1 -1
- package/dist/form/components/fields/lazy-date-picker-control.d.ts +2 -0
- package/dist/form/components/fields/lazy-date-picker-control.js +17 -0
- package/dist/form/components/fields/lazy-date-picker-control.js.map +1 -0
- package/dist/form/components/fields/number-input.js +1 -1
- package/dist/form/components/fields/rich-editor.js +3 -3
- package/dist/form/components/fields/rich-editor.js.map +1 -1
- package/dist/form/components/fields/row-action-menu.js +2 -2
- package/dist/form/components/fields/row-action-menu.js.map +1 -1
- package/dist/form/components/fields/select.js +1 -1
- package/dist/form/components/fields/select.js.map +1 -1
- package/dist/form/components/fields/table-rows.js +2 -2
- package/dist/form/components/fields/table-rows.js.map +1 -1
- package/dist/form/components/fields/text-input.js +1 -1
- package/dist/form/components/fields/time-input.d.ts +2 -0
- package/dist/form/components/fields/time-input.js +30 -0
- package/dist/form/components/fields/time-input.js.map +1 -0
- package/dist/form/components/fields/toggle.d.ts +2 -0
- package/dist/form/components/fields/toggle.js +65 -0
- package/dist/form/components/fields/toggle.js.map +1 -0
- package/dist/form/components/index.d.ts +3 -0
- package/dist/form/components/index.js +4 -1
- package/dist/form/components/types.d.ts +1 -1
- package/dist/form/components/use-controlled-field.d.ts +2 -0
- package/dist/form/components/use-controlled-field.js +6 -2
- package/dist/form/components/use-controlled-field.js.map +1 -1
- package/dist/form/index.js +4 -1
- package/dist/form/index.js.map +1 -1
- package/dist/lattice.css +6 -0
- package/dist/layout/callouts.js +1 -1
- package/dist/layout/callouts.js.map +1 -1
- package/dist/lib/utils.js +11 -1
- package/dist/lib/utils.js.map +1 -1
- package/dist/remote/components/data-list.js +3 -3
- package/dist/remote/components/data-list.js.map +1 -1
- package/dist/table/components/bulk-bar.js +2 -2
- package/dist/table/components/bulk-bar.js.map +1 -1
- package/dist/table/components/column-filter-control.js +5 -5
- package/dist/table/components/column-filter-control.js.map +1 -1
- package/dist/table/components/filter-bar.js +3 -3
- package/dist/table/components/filter-bar.js.map +1 -1
- package/dist/table/components/filter-controls.js +9 -9
- package/dist/table/components/filter-controls.js.map +1 -1
- package/dist/table/components/filter-stack-bar.js +1 -1
- package/dist/table/components/filter-stack-bar.js.map +1 -1
- package/dist/table/components/filter-value-input.js +5 -5
- package/dist/table/components/filter-value-input.js.map +1 -1
- package/dist/table/components/pagination.js +8 -8
- package/dist/table/components/pagination.js.map +1 -1
- package/dist/table/components/table.js +4 -4
- package/dist/table/components/table.js.map +1 -1
- package/dist/table/query.js +1 -1
- package/dist/table/query.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 +97 -2
- package/package.json +8 -2
- package/resources/icons/calendar.svg +18 -0
- package/resources/icons/chevron-left.svg +15 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"callouts.js","names":[],"sources":["../../resources/js/layout/callouts.tsx"],"sourcesContent":["import { Icon } from \"@lattice-php/lattice/icons\";\nimport { useEffect, useState } from \"react\";\nimport { RenderNode } from \"@lattice-php/lattice/core/renderer\";\nimport type { Callout } from \"@lattice-php/lattice/types/generated\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { onCallout } from \"@lattice-php/lattice/toast/callout\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { variantStyles } from \"@lattice-php/lattice/toast/variant-styles\";\n\ntype CalloutItem = Callout & { id: number };\n\nlet nextId = 0;\n\nconst Callouts: RendererComponent<\"callouts\"> = () => {\n const { t } = useT(\"lattice\");\n const [callouts, setCallouts] = useState<CalloutItem[]>([]);\n\n useEffect(\n () =>\n onCallout((callout) => {\n setCallouts((current) => [...current, { ...callout, id: nextId++ }]);\n }),\n [],\n );\n\n function dismiss(id: number): void {\n setCallouts((current) => current.filter((callout) => callout.id !== id));\n }\n\n if (callouts.length === 0) {\n return null;\n }\n\n return (\n <div className=\"flex w-full flex-col gap-2\">\n {callouts.map((callout) => (\n <div\n key={callout.id}\n role=\"status\"\n data-test={`callout-${callout.variant}`}\n className={cn(\n \"flex items-start gap-3 rounded-lt border border-l-4 border-lt-border bg-lt-popover p-4 text-lt-popover-fg\",\n variantStyles[callout.variant].accent,\n )}\n >\n {variantStyles[callout.variant].icon}\n <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n {callout.title ? (\n <p className=\"text-sm font-medium text-lt-fg\">{callout.title}</p>\n ) : null}\n <p className=\"text-sm text-lt-fg\">{callout.message}</p>\n {callout.action ? (\n <div className=\"flex flex-wrap gap-2\">\n <RenderNode node={callout.action} />\n </div>\n ) : null}\n </div>\n {callout.dismissible ? (\n <button\n type=\"button\"\n aria-label={t(\"
|
|
1
|
+
{"version":3,"file":"callouts.js","names":[],"sources":["../../resources/js/layout/callouts.tsx"],"sourcesContent":["import { Icon } from \"@lattice-php/lattice/icons\";\nimport { useEffect, useState } from \"react\";\nimport { RenderNode } from \"@lattice-php/lattice/core/renderer\";\nimport type { Callout } from \"@lattice-php/lattice/types/generated\";\nimport type { RendererComponent } from \"@lattice-php/lattice/core/types\";\nimport { onCallout } from \"@lattice-php/lattice/toast/callout\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { variantStyles } from \"@lattice-php/lattice/toast/variant-styles\";\n\ntype CalloutItem = Callout & { id: number };\n\nlet nextId = 0;\n\nconst Callouts: RendererComponent<\"callouts\"> = () => {\n const { t } = useT(\"lattice\");\n const [callouts, setCallouts] = useState<CalloutItem[]>([]);\n\n useEffect(\n () =>\n onCallout((callout) => {\n setCallouts((current) => [...current, { ...callout, id: nextId++ }]);\n }),\n [],\n );\n\n function dismiss(id: number): void {\n setCallouts((current) => current.filter((callout) => callout.id !== id));\n }\n\n if (callouts.length === 0) {\n return null;\n }\n\n return (\n <div className=\"flex w-full flex-col gap-2\">\n {callouts.map((callout) => (\n <div\n key={callout.id}\n role=\"status\"\n data-test={`callout-${callout.variant}`}\n className={cn(\n \"flex items-start gap-3 rounded-lt border border-l-4 border-lt-border bg-lt-popover p-4 text-lt-popover-fg\",\n variantStyles[callout.variant].accent,\n )}\n >\n {variantStyles[callout.variant].icon}\n <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n {callout.title ? (\n <p className=\"text-sm font-medium text-lt-fg\">{callout.title}</p>\n ) : null}\n <p className=\"text-sm text-lt-fg\">{callout.message}</p>\n {callout.action ? (\n <div className=\"flex flex-wrap gap-2\">\n <RenderNode node={callout.action} />\n </div>\n ) : null}\n </div>\n {callout.dismissible ? (\n <button\n type=\"button\"\n aria-label={t(\"common.dismiss\", \"Dismiss\")}\n data-test=\"callout-dismiss\"\n className=\"shrink-0 rounded-lt-sm p-1 text-lt-muted-fg transition-colors hover:bg-lt-muted hover:text-lt-fg\"\n onClick={() => dismiss(callout.id)}\n >\n <Icon name=\"x\" className=\"size-lt-icon-md\" />\n </button>\n ) : null}\n </div>\n ))}\n </div>\n );\n};\n\nexport default Callouts;\n"],"mappings":";;;;;;;;;AAYA,IAAI,SAAS;AAEb,IAAM,iBAAgD;CACpD,MAAM,EAAE,MAAM,KAAK,SAAS;CAC5B,MAAM,CAAC,UAAU,eAAe,SAAwB,CAAC,CAAC;CAE1D,gBAEI,WAAW,YAAY;EACrB,aAAa,YAAY,CAAC,GAAG,SAAS;GAAE,GAAG;GAAS,IAAI;EAAS,CAAC,CAAC;CACrE,CAAC,GACH,CAAC,CACH;CAEA,SAAS,QAAQ,IAAkB;EACjC,aAAa,YAAY,QAAQ,QAAQ,YAAY,QAAQ,OAAO,EAAE,CAAC;CACzE;CAEA,IAAI,SAAS,WAAW,GACtB,OAAO;CAGT,OACE,oBAAC,OAAD;EAAK,WAAU;YACZ,SAAS,KAAK,YACb,qBAAC,OAAD;GAEE,MAAK;GACL,aAAW,WAAW,QAAQ;GAC9B,WAAW,GACT,6GACA,cAAc,QAAQ,SAAS,MACjC;aAPF;IASG,cAAc,QAAQ,SAAS;IAChC,qBAAC,OAAD;KAAK,WAAU;eAAf;MACG,QAAQ,QACP,oBAAC,KAAD;OAAG,WAAU;iBAAkC,QAAQ;MAAS,CAAA,IAC9D;MACJ,oBAAC,KAAD;OAAG,WAAU;iBAAsB,QAAQ;MAAW,CAAA;MACrD,QAAQ,SACP,oBAAC,OAAD;OAAK,WAAU;iBACb,oBAAC,YAAD,EAAY,MAAM,QAAQ,OAAS,CAAA;MAChC,CAAA,IACH;KACD;;IACJ,QAAQ,cACP,oBAAC,UAAD;KACE,MAAK;KACL,cAAY,EAAE,kBAAkB,SAAS;KACzC,aAAU;KACV,WAAU;KACV,eAAe,QAAQ,QAAQ,EAAE;eAEjC,oBAAC,MAAD;MAAM,MAAK;MAAI,WAAU;KAAmB,CAAA;IACtC,CAAA,IACN;GACD;KA/BE,QAAQ,EA+BV,CACN;CACE,CAAA;AAET"}
|
package/dist/lib/utils.js
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { clsx } from "clsx";
|
|
2
|
-
import {
|
|
2
|
+
import { extendTailwindMerge } from "tailwind-merge";
|
|
3
3
|
//#region resources/js/lib/utils.ts
|
|
4
|
+
var twMerge = extendTailwindMerge({ extend: { classGroups: { "lt-icon-size": [{ size: [{ lt: [{ icon: [
|
|
5
|
+
"xs",
|
|
6
|
+
"sm",
|
|
7
|
+
"md",
|
|
8
|
+
"lg",
|
|
9
|
+
"xl",
|
|
10
|
+
"2xl",
|
|
11
|
+
"3xl",
|
|
12
|
+
"4xl"
|
|
13
|
+
] }] }] }] } } });
|
|
4
14
|
function cn(...inputs) {
|
|
5
15
|
return twMerge(clsx(inputs));
|
|
6
16
|
}
|
package/dist/lib/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","names":[],"sources":["../../resources/js/lib/utils.ts"],"sourcesContent":["import { clsx } from \"clsx\";\nimport type { ClassValue } from \"clsx\";\nimport {
|
|
1
|
+
{"version":3,"file":"utils.js","names":[],"sources":["../../resources/js/lib/utils.ts"],"sourcesContent":["import { clsx } from \"clsx\";\nimport type { ClassValue } from \"clsx\";\nimport { extendTailwindMerge } from \"tailwind-merge\";\n\n// The sprite renderer always applies `size-lt-icon-md` as a baseline, and these\n// custom utilities are invisible to tailwind-merge's stock config, so an explicit\n// `size-lt-icon-*` would otherwise be left to fight the baseline on source order.\n// Registering them as one conflicting group lets the explicit size always win.\nconst twMerge = extendTailwindMerge<\"lt-icon-size\">({\n extend: {\n classGroups: {\n \"lt-icon-size\": [\n { size: [{ lt: [{ icon: [\"xs\", \"sm\", \"md\", \"lg\", \"xl\", \"2xl\", \"3xl\", \"4xl\"] }] }] },\n ],\n },\n },\n});\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n"],"mappings":";;;AAQA,IAAM,UAAU,oBAAoC,EAClD,QAAQ,EACN,aAAa,EACX,gBAAgB,CACd,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM;CAAC;CAAM;CAAM;CAAM;CAAM;CAAM;CAAO;CAAO;AAAK,EAAE,CAAC,EAAE,CAAC,EAAE,CACpF,EACF,EACF,EACF,CAAC;AAED,SAAgB,GAAG,GAAG,QAAsB;CAC1C,OAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B"}
|
|
@@ -73,13 +73,13 @@ function RemoteDataList({ props }) {
|
|
|
73
73
|
});
|
|
74
74
|
if (loading || !props.remote) return /* @__PURE__ */ jsx("div", {
|
|
75
75
|
className: "rounded-lt border border-lt-border p-3 text-sm",
|
|
76
|
-
children: t("remote.data-list.loading", "Loading...")
|
|
76
|
+
children: t("common.remote.data-list.loading", "Loading...")
|
|
77
77
|
});
|
|
78
78
|
return /* @__PURE__ */ jsxs("div", {
|
|
79
79
|
className: "rounded-lt border border-lt-border bg-lt-bg",
|
|
80
80
|
children: [/* @__PURE__ */ jsx("div", {
|
|
81
81
|
className: "border-b border-lt-border px-3 py-2 text-sm font-medium",
|
|
82
|
-
children: t("remote.data-list.title", "Remote data")
|
|
82
|
+
children: t("common.remote.data-list.title", "Remote data")
|
|
83
83
|
}), /* @__PURE__ */ jsxs("div", {
|
|
84
84
|
className: "divide-y divide-lt-border",
|
|
85
85
|
children: [rows.map((row, index) => {
|
|
@@ -89,7 +89,7 @@ function RemoteDataList({ props }) {
|
|
|
89
89
|
}, label(row.id) || index);
|
|
90
90
|
}), rows.length === 0 ? /* @__PURE__ */ jsx("div", {
|
|
91
91
|
className: "px-3 py-2 text-sm text-lt-muted-fg",
|
|
92
|
-
children: props.emptyLabel ?? t("remote.data-list.empty", "No data")
|
|
92
|
+
children: props.emptyLabel ?? t("common.remote.data-list.empty", "No data")
|
|
93
93
|
}) : null]
|
|
94
94
|
})]
|
|
95
95
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-list.js","names":[],"sources":["../../../resources/js/remote/components/data-list.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { Renderer } from \"@lattice-php/lattice/core/renderer\";\nimport type { Node, NodeProps, RendererComponent, Schema } from \"@lattice-php/lattice/core/types\";\nimport { remoteJson } from \"@lattice-php/lattice/core/api\";\nimport type { DataList as DataListProps } from \"@lattice-php/lattice/types/generated\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\n\ntype RemotePayload = {\n data?: Array<Record<string, unknown>>;\n};\n\ntype RemoteRow = Record<string, unknown>;\n\ntype DataBindings = Record<string, string>;\n\ntype RemoteDataListProps = DataListProps & {\n schema?: Schema;\n};\n\nfunction label(value: unknown): string {\n if (value === null || value === undefined) {\n return \"\";\n }\n\n return String(value);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction dataBindings(value: unknown): DataBindings {\n if (!isRecord(value)) {\n return {};\n }\n\n return Object.fromEntries(\n Object.entries(value).filter((entry): entry is [string, string] => {\n return typeof entry[1] === \"string\";\n }),\n );\n}\n\nfunction rowValue(row: RemoteRow, key: string): unknown {\n if (key in row) {\n return row[key];\n }\n\n return key.split(\".\").reduce<unknown>((value, segment) => {\n return isRecord(value) ? value[segment] : undefined;\n }, row);\n}\n\nfunction materializeProps(props: unknown, row: RemoteRow): NodeProps {\n if (!isRecord(props)) {\n return {};\n }\n\n const { dataBindings: bindings, ...materialized } = props;\n\n for (const [prop, key] of Object.entries(dataBindings(bindings))) {\n const value = rowValue(row, key);\n\n if (value !== undefined) {\n materialized[prop] = value;\n }\n }\n\n return materialized;\n}\n\nfunction materializeNode(node: Node, row: RemoteRow): Node {\n return {\n ...node,\n props: materializeProps(node.props, row),\n schema: node.schema?.map((child) => materializeNode(child, row)),\n } as Node;\n}\n\nfunction materializeSchema(schema: Schema | undefined, row: RemoteRow): Schema {\n return schema?.map((node) => materializeNode(node, row)) ?? [];\n}\n\nexport function RemoteDataList({ props }: { props: RemoteDataListProps }) {\n const { t } = useT(\"lattice\");\n const [rows, setRows] = useState<RemoteRow[]>([]);\n const [error, setError] = useState<string | null>(null);\n const [loading, setLoading] = useState(false);\n\n useEffect(() => {\n if (!props.remote || !props.dataEndpoint) {\n setRows([]);\n setLoading(false);\n\n return;\n }\n\n let cancelled = false;\n setError(null);\n setLoading(true);\n\n void remoteJson<RemotePayload>(props.dataEndpoint, { remote: props.remote })\n .then((payload) => {\n if (!cancelled) {\n setRows(payload.data ?? []);\n }\n })\n .catch((caught: unknown) => {\n if (!cancelled) {\n setError(caught instanceof Error ? caught.message : String(caught));\n }\n })\n .finally(() => {\n if (!cancelled) {\n setLoading(false);\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, [props.dataEndpoint, props.remote]);\n\n if (error) {\n return <div className=\"rounded-lt border border-lt-danger/40 p-3 text-sm\">{error}</div>;\n }\n\n if (loading || !props.remote) {\n return (\n <div className=\"rounded-lt border border-lt-border p-3 text-sm\">\n {t(\"remote.data-list.loading\", \"Loading...\")}\n </div>\n );\n }\n\n return (\n <div className=\"rounded-lt border border-lt-border bg-lt-bg\">\n <div className=\"border-b border-lt-border px-3 py-2 text-sm font-medium\">\n {t(\"remote.data-list.title\", \"Remote data\")}\n </div>\n <div className=\"divide-y divide-lt-border\">\n {rows.map((row, index) => {\n return (\n <div className=\"px-3 py-2\" key={label(row.id) || index}>\n <Renderer nodes={materializeSchema(props.schema, row)} />\n </div>\n );\n })}\n {rows.length === 0 ? (\n <div className=\"px-3 py-2 text-sm text-lt-muted-fg\">\n {props.emptyLabel ?? t(\"remote.data-list.empty\", \"No data\")}\n </div>\n ) : null}\n </div>\n </div>\n );\n}\n\nexport const DataList: RendererComponent<\"remote.data-list\"> = ({ node }) => {\n return <RemoteDataList props={{ ...node.props, schema: node.schema }} />;\n};\n\nexport default DataList;\n"],"mappings":";;;;;;AAmBA,SAAS,MAAM,OAAwB;CACrC,IAAI,UAAU,QAAQ,UAAU,KAAA,GAC9B,OAAO;CAGT,OAAO,OAAO,KAAK;AACrB;AAEA,SAAS,SAAS,OAAkD;CAClE,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,aAAa,OAA8B;CAClD,IAAI,CAAC,SAAS,KAAK,GACjB,OAAO,CAAC;CAGV,OAAO,OAAO,YACZ,OAAO,QAAQ,KAAK,EAAE,QAAQ,UAAqC;EACjE,OAAO,OAAO,MAAM,OAAO;CAC7B,CAAC,CACH;AACF;AAEA,SAAS,SAAS,KAAgB,KAAsB;CACtD,IAAI,OAAO,KACT,OAAO,IAAI;CAGb,OAAO,IAAI,MAAM,GAAG,EAAE,QAAiB,OAAO,YAAY;EACxD,OAAO,SAAS,KAAK,IAAI,MAAM,WAAW,KAAA;CAC5C,GAAG,GAAG;AACR;AAEA,SAAS,iBAAiB,OAAgB,KAA2B;CACnE,IAAI,CAAC,SAAS,KAAK,GACjB,OAAO,CAAC;CAGV,MAAM,EAAE,cAAc,UAAU,GAAG,iBAAiB;CAEpD,KAAK,MAAM,CAAC,MAAM,QAAQ,OAAO,QAAQ,aAAa,QAAQ,CAAC,GAAG;EAChE,MAAM,QAAQ,SAAS,KAAK,GAAG;EAE/B,IAAI,UAAU,KAAA,GACZ,aAAa,QAAQ;CAEzB;CAEA,OAAO;AACT;AAEA,SAAS,gBAAgB,MAAY,KAAsB;CACzD,OAAO;EACL,GAAG;EACH,OAAO,iBAAiB,KAAK,OAAO,GAAG;EACvC,QAAQ,KAAK,QAAQ,KAAK,UAAU,gBAAgB,OAAO,GAAG,CAAC;CACjE;AACF;AAEA,SAAS,kBAAkB,QAA4B,KAAwB;CAC7E,OAAO,QAAQ,KAAK,SAAS,gBAAgB,MAAM,GAAG,CAAC,KAAK,CAAC;AAC/D;AAEA,SAAgB,eAAe,EAAE,SAAyC;CACxE,MAAM,EAAE,MAAM,KAAK,SAAS;CAC5B,MAAM,CAAC,MAAM,WAAW,SAAsB,CAAC,CAAC;CAChD,MAAM,CAAC,OAAO,YAAY,SAAwB,IAAI;CACtD,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAE5C,gBAAgB;EACd,IAAI,CAAC,MAAM,UAAU,CAAC,MAAM,cAAc;GACxC,QAAQ,CAAC,CAAC;GACV,WAAW,KAAK;GAEhB;EACF;EAEA,IAAI,YAAY;EAChB,SAAS,IAAI;EACb,WAAW,IAAI;EAEf,WAA+B,MAAM,cAAc,EAAE,QAAQ,MAAM,OAAO,CAAC,EACxE,MAAM,YAAY;GACjB,IAAI,CAAC,WACH,QAAQ,QAAQ,QAAQ,CAAC,CAAC;EAE9B,CAAC,EACA,OAAO,WAAoB;GAC1B,IAAI,CAAC,WACH,SAAS,kBAAkB,QAAQ,OAAO,UAAU,OAAO,MAAM,CAAC;EAEtE,CAAC,EACA,cAAc;GACb,IAAI,CAAC,WACH,WAAW,KAAK;EAEpB,CAAC;EAEH,aAAa;GACX,YAAY;EACd;CACF,GAAG,CAAC,MAAM,cAAc,MAAM,MAAM,CAAC;CAErC,IAAI,OACF,OAAO,oBAAC,OAAD;EAAK,WAAU;YAAqD;CAAW,CAAA;CAGxF,IAAI,WAAW,CAAC,MAAM,QACpB,OACE,oBAAC,OAAD;EAAK,WAAU;YACZ,EAAE,
|
|
1
|
+
{"version":3,"file":"data-list.js","names":[],"sources":["../../../resources/js/remote/components/data-list.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { Renderer } from \"@lattice-php/lattice/core/renderer\";\nimport type { Node, NodeProps, RendererComponent, Schema } from \"@lattice-php/lattice/core/types\";\nimport { remoteJson } from \"@lattice-php/lattice/core/api\";\nimport type { DataList as DataListProps } from \"@lattice-php/lattice/types/generated\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\n\ntype RemotePayload = {\n data?: Array<Record<string, unknown>>;\n};\n\ntype RemoteRow = Record<string, unknown>;\n\ntype DataBindings = Record<string, string>;\n\ntype RemoteDataListProps = DataListProps & {\n schema?: Schema;\n};\n\nfunction label(value: unknown): string {\n if (value === null || value === undefined) {\n return \"\";\n }\n\n return String(value);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction dataBindings(value: unknown): DataBindings {\n if (!isRecord(value)) {\n return {};\n }\n\n return Object.fromEntries(\n Object.entries(value).filter((entry): entry is [string, string] => {\n return typeof entry[1] === \"string\";\n }),\n );\n}\n\nfunction rowValue(row: RemoteRow, key: string): unknown {\n if (key in row) {\n return row[key];\n }\n\n return key.split(\".\").reduce<unknown>((value, segment) => {\n return isRecord(value) ? value[segment] : undefined;\n }, row);\n}\n\nfunction materializeProps(props: unknown, row: RemoteRow): NodeProps {\n if (!isRecord(props)) {\n return {};\n }\n\n const { dataBindings: bindings, ...materialized } = props;\n\n for (const [prop, key] of Object.entries(dataBindings(bindings))) {\n const value = rowValue(row, key);\n\n if (value !== undefined) {\n materialized[prop] = value;\n }\n }\n\n return materialized;\n}\n\nfunction materializeNode(node: Node, row: RemoteRow): Node {\n return {\n ...node,\n props: materializeProps(node.props, row),\n schema: node.schema?.map((child) => materializeNode(child, row)),\n } as Node;\n}\n\nfunction materializeSchema(schema: Schema | undefined, row: RemoteRow): Schema {\n return schema?.map((node) => materializeNode(node, row)) ?? [];\n}\n\nexport function RemoteDataList({ props }: { props: RemoteDataListProps }) {\n const { t } = useT(\"lattice\");\n const [rows, setRows] = useState<RemoteRow[]>([]);\n const [error, setError] = useState<string | null>(null);\n const [loading, setLoading] = useState(false);\n\n useEffect(() => {\n if (!props.remote || !props.dataEndpoint) {\n setRows([]);\n setLoading(false);\n\n return;\n }\n\n let cancelled = false;\n setError(null);\n setLoading(true);\n\n void remoteJson<RemotePayload>(props.dataEndpoint, { remote: props.remote })\n .then((payload) => {\n if (!cancelled) {\n setRows(payload.data ?? []);\n }\n })\n .catch((caught: unknown) => {\n if (!cancelled) {\n setError(caught instanceof Error ? caught.message : String(caught));\n }\n })\n .finally(() => {\n if (!cancelled) {\n setLoading(false);\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, [props.dataEndpoint, props.remote]);\n\n if (error) {\n return <div className=\"rounded-lt border border-lt-danger/40 p-3 text-sm\">{error}</div>;\n }\n\n if (loading || !props.remote) {\n return (\n <div className=\"rounded-lt border border-lt-border p-3 text-sm\">\n {t(\"common.remote.data-list.loading\", \"Loading...\")}\n </div>\n );\n }\n\n return (\n <div className=\"rounded-lt border border-lt-border bg-lt-bg\">\n <div className=\"border-b border-lt-border px-3 py-2 text-sm font-medium\">\n {t(\"common.remote.data-list.title\", \"Remote data\")}\n </div>\n <div className=\"divide-y divide-lt-border\">\n {rows.map((row, index) => {\n return (\n <div className=\"px-3 py-2\" key={label(row.id) || index}>\n <Renderer nodes={materializeSchema(props.schema, row)} />\n </div>\n );\n })}\n {rows.length === 0 ? (\n <div className=\"px-3 py-2 text-sm text-lt-muted-fg\">\n {props.emptyLabel ?? t(\"common.remote.data-list.empty\", \"No data\")}\n </div>\n ) : null}\n </div>\n </div>\n );\n}\n\nexport const DataList: RendererComponent<\"remote.data-list\"> = ({ node }) => {\n return <RemoteDataList props={{ ...node.props, schema: node.schema }} />;\n};\n\nexport default DataList;\n"],"mappings":"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"}
|
|
@@ -49,14 +49,14 @@ function BulkBar({ actions, selectedKeys, allMatching, total, query, canSelectAl
|
|
|
49
49
|
children: [
|
|
50
50
|
/* @__PURE__ */ jsx("span", {
|
|
51
51
|
className: "font-medium",
|
|
52
|
-
children: allMatching ? t("bulk.allSelected", "All {{count}} selected", { count }) : t("bulk.selected", "{{count}} selected", { count })
|
|
52
|
+
children: allMatching ? t("table.bulk.allSelected", "All {{count}} selected", { count }) : t("table.bulk.selected", "{{count}} selected", { count })
|
|
53
53
|
}),
|
|
54
54
|
canSelectAllMatching && /* @__PURE__ */ jsx("button", {
|
|
55
55
|
type: "button",
|
|
56
56
|
"data-test": "bulk-select-all-matching",
|
|
57
57
|
className: "font-medium text-lt-primary underline underline-offset-2",
|
|
58
58
|
onClick: onSelectAllMatching,
|
|
59
|
-
children: t("bulk.selectAllMatching", "Select all {{total}} matching", { total })
|
|
59
|
+
children: t("table.bulk.selectAllMatching", "Select all {{total}} matching", { total })
|
|
60
60
|
}),
|
|
61
61
|
/* @__PURE__ */ jsx("div", {
|
|
62
62
|
className: "flex flex-wrap items-center gap-2",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bulk-bar.js","names":[],"sources":["../../../resources/js/table/components/bulk-bar.tsx"],"sourcesContent":["import { useHttp } from \"@inertiajs/react\";\nimport { useState } from \"react\";\nimport { ActionForm } from \"@lattice-php/lattice/action/components/action-form\";\nimport { runAction } from \"@lattice-php/lattice/action/run-action\";\nimport { getActionEffects } from \"@lattice-php/lattice/effects/dispatch\";\nimport type { ActionResponse } from \"@lattice-php/lattice/effects/dispatch\";\nimport { useEffectDispatcher } from \"@lattice-php/lattice/effects/use-effect-dispatcher\";\nimport { withHeaders } from \"@lattice-php/lattice/core/headers\";\nimport { Button } from \"@lattice-php/lattice/core/components/button\";\nimport { ConfirmDialog } from \"@lattice-php/lattice/core/components/confirm-dialog\";\nimport { Spinner } from \"@lattice-php/lattice/core/components/spinner\";\nimport { prefixedTestId } from \"@lattice-php/lattice/core/test-id\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport type { BulkAction } from \"../bulk\";\n\ntype BulkData = {\n allMatching?: boolean;\n selected?: string[];\n};\n\nexport function BulkBar({\n actions,\n selectedKeys,\n allMatching,\n total,\n query,\n canSelectAllMatching,\n onSelectAllMatching,\n onCompleted,\n}: {\n actions: BulkAction[];\n selectedKeys: string[];\n allMatching: boolean;\n total?: number;\n query: Record<string, unknown>;\n canSelectAllMatching: boolean;\n onSelectAllMatching: () => void;\n onCompleted: () => void;\n}) {\n const { t } = useT(\"lattice\");\n const http = useHttp<BulkData, ActionResponse>({});\n const dispatch = useEffectDispatcher();\n const [confirming, setConfirming] = useState<BulkAction | null>(null);\n const [filling, setFilling] = useState<BulkAction | null>(null);\n\n const selectionPayload = (): Record<string, unknown> =>\n allMatching ? { allMatching: true, ...query } : { selected: selectedKeys };\n\n async function submit(action: BulkAction): Promise<void> {\n http.transform((data) => ({ ...data, ...selectionPayload() }));\n\n const ok = await runAction(\n () => http[action.method](action.endpoint, { headers: withHeaders(action.ref ?? \"\") }),\n dispatch,\n );\n\n if (ok) {\n setConfirming(null);\n onCompleted();\n }\n }\n\n function run(action: BulkAction): void {\n if (action.form) {\n setFilling(action);\n\n return;\n }\n\n if (action.confirmation) {\n setConfirming(action);\n\n return;\n }\n\n void submit(action);\n }\n\n const count = allMatching ? (total ?? selectedKeys.length) : selectedKeys.length;\n\n return (\n <div className=\"flex flex-wrap items-center gap-3 border-b border-lt-border bg-lt-muted/50 p-4 text-sm\">\n <span className=\"font-medium\">\n {allMatching\n ? t(\"bulk.allSelected\", \"All {{count}} selected\", { count })\n : t(\"bulk.selected\", \"{{count}} selected\", { count })}\n </span>\n {canSelectAllMatching && (\n <button\n type=\"button\"\n data-test=\"bulk-select-all-matching\"\n className=\"font-medium text-lt-primary underline underline-offset-2\"\n onClick={onSelectAllMatching}\n >\n {t(\"bulk.selectAllMatching\", \"Select all {{total}} matching\", { total })}\n </button>\n )}\n <div className=\"flex flex-wrap items-center gap-2\">\n {actions.map((action) => (\n <Button\n key={action.id}\n type=\"button\"\n data-test={prefixedTestId(\"bulk-action\", action.id)}\n variant={action.variant}\n disabled={http.processing}\n onClick={() => run(action)}\n >\n {http.processing && <Spinner />}\n {action.label}\n </Button>\n ))}\n </div>\n\n {confirming?.confirmation && (\n <ConfirmDialog\n title={confirming.confirmation.title ?? confirming.label}\n description={confirming.confirmation.description ?? undefined}\n confirmLabel={confirming.confirmation.confirmLabel ?? confirming.label}\n cancelLabel={confirming.confirmation.cancelLabel ?? \"Cancel\"}\n confirmVariant={confirming.variant}\n processing={http.processing}\n onConfirm={() => void submit(confirming)}\n onCancel={() => setConfirming(null)}\n />\n )}\n\n {filling?.form && (\n <ActionForm\n cancelLabel={filling.confirmation?.cancelLabel ?? \"Cancel\"}\n componentRef={filling.ref}\n description={filling.confirmation?.description ?? undefined}\n endpoint={filling.endpoint}\n extraData={selectionPayload()}\n formNode={filling.form}\n method={filling.method}\n onClose={() => setFilling(null)}\n onSuccess={(response) => {\n dispatch(getActionEffects(response.effects));\n setFilling(null);\n onCompleted();\n }}\n submitLabel={filling.confirmation?.confirmLabel ?? filling.label}\n title={filling.confirmation?.title ?? filling.label}\n />\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAoBA,SAAgB,QAAQ,EACtB,SACA,cACA,aACA,OACA,OACA,sBACA,qBACA,eAUC;CACD,MAAM,EAAE,MAAM,KAAK,SAAS;CAC5B,MAAM,OAAO,QAAkC,CAAC,CAAC;CACjD,MAAM,WAAW,oBAAoB;CACrC,MAAM,CAAC,YAAY,iBAAiB,SAA4B,IAAI;CACpE,MAAM,CAAC,SAAS,cAAc,SAA4B,IAAI;CAE9D,MAAM,yBACJ,cAAc;EAAE,aAAa;EAAM,GAAG;CAAM,IAAI,EAAE,UAAU,aAAa;CAE3E,eAAe,OAAO,QAAmC;EACvD,KAAK,WAAW,UAAU;GAAE,GAAG;GAAM,GAAG,iBAAiB;EAAE,EAAE;EAO7D,IAAI,MALa,gBACT,KAAK,OAAO,QAAQ,OAAO,UAAU,EAAE,SAAS,YAAY,OAAO,OAAO,EAAE,EAAE,CAAC,GACrF,QACF,GAEQ;GACN,cAAc,IAAI;GAClB,YAAY;EACd;CACF;CAEA,SAAS,IAAI,QAA0B;EACrC,IAAI,OAAO,MAAM;GACf,WAAW,MAAM;GAEjB;EACF;EAEA,IAAI,OAAO,cAAc;GACvB,cAAc,MAAM;GAEpB;EACF;EAEA,OAAY,MAAM;CACpB;CAEA,MAAM,QAAQ,cAAe,SAAS,aAAa,SAAU,aAAa;CAE1E,OACE,qBAAC,OAAD;EAAK,WAAU;YAAf;GACE,oBAAC,QAAD;IAAM,WAAU;cACb,cACG,EAAE,
|
|
1
|
+
{"version":3,"file":"bulk-bar.js","names":[],"sources":["../../../resources/js/table/components/bulk-bar.tsx"],"sourcesContent":["import { useHttp } from \"@inertiajs/react\";\nimport { useState } from \"react\";\nimport { ActionForm } from \"@lattice-php/lattice/action/components/action-form\";\nimport { runAction } from \"@lattice-php/lattice/action/run-action\";\nimport { getActionEffects } from \"@lattice-php/lattice/effects/dispatch\";\nimport type { ActionResponse } from \"@lattice-php/lattice/effects/dispatch\";\nimport { useEffectDispatcher } from \"@lattice-php/lattice/effects/use-effect-dispatcher\";\nimport { withHeaders } from \"@lattice-php/lattice/core/headers\";\nimport { Button } from \"@lattice-php/lattice/core/components/button\";\nimport { ConfirmDialog } from \"@lattice-php/lattice/core/components/confirm-dialog\";\nimport { Spinner } from \"@lattice-php/lattice/core/components/spinner\";\nimport { prefixedTestId } from \"@lattice-php/lattice/core/test-id\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport type { BulkAction } from \"../bulk\";\n\ntype BulkData = {\n allMatching?: boolean;\n selected?: string[];\n};\n\nexport function BulkBar({\n actions,\n selectedKeys,\n allMatching,\n total,\n query,\n canSelectAllMatching,\n onSelectAllMatching,\n onCompleted,\n}: {\n actions: BulkAction[];\n selectedKeys: string[];\n allMatching: boolean;\n total?: number;\n query: Record<string, unknown>;\n canSelectAllMatching: boolean;\n onSelectAllMatching: () => void;\n onCompleted: () => void;\n}) {\n const { t } = useT(\"lattice\");\n const http = useHttp<BulkData, ActionResponse>({});\n const dispatch = useEffectDispatcher();\n const [confirming, setConfirming] = useState<BulkAction | null>(null);\n const [filling, setFilling] = useState<BulkAction | null>(null);\n\n const selectionPayload = (): Record<string, unknown> =>\n allMatching ? { allMatching: true, ...query } : { selected: selectedKeys };\n\n async function submit(action: BulkAction): Promise<void> {\n http.transform((data) => ({ ...data, ...selectionPayload() }));\n\n const ok = await runAction(\n () => http[action.method](action.endpoint, { headers: withHeaders(action.ref ?? \"\") }),\n dispatch,\n );\n\n if (ok) {\n setConfirming(null);\n onCompleted();\n }\n }\n\n function run(action: BulkAction): void {\n if (action.form) {\n setFilling(action);\n\n return;\n }\n\n if (action.confirmation) {\n setConfirming(action);\n\n return;\n }\n\n void submit(action);\n }\n\n const count = allMatching ? (total ?? selectedKeys.length) : selectedKeys.length;\n\n return (\n <div className=\"flex flex-wrap items-center gap-3 border-b border-lt-border bg-lt-muted/50 p-4 text-sm\">\n <span className=\"font-medium\">\n {allMatching\n ? t(\"table.bulk.allSelected\", \"All {{count}} selected\", { count })\n : t(\"table.bulk.selected\", \"{{count}} selected\", { count })}\n </span>\n {canSelectAllMatching && (\n <button\n type=\"button\"\n data-test=\"bulk-select-all-matching\"\n className=\"font-medium text-lt-primary underline underline-offset-2\"\n onClick={onSelectAllMatching}\n >\n {t(\"table.bulk.selectAllMatching\", \"Select all {{total}} matching\", { total })}\n </button>\n )}\n <div className=\"flex flex-wrap items-center gap-2\">\n {actions.map((action) => (\n <Button\n key={action.id}\n type=\"button\"\n data-test={prefixedTestId(\"bulk-action\", action.id)}\n variant={action.variant}\n disabled={http.processing}\n onClick={() => run(action)}\n >\n {http.processing && <Spinner />}\n {action.label}\n </Button>\n ))}\n </div>\n\n {confirming?.confirmation && (\n <ConfirmDialog\n title={confirming.confirmation.title ?? confirming.label}\n description={confirming.confirmation.description ?? undefined}\n confirmLabel={confirming.confirmation.confirmLabel ?? confirming.label}\n cancelLabel={confirming.confirmation.cancelLabel ?? \"Cancel\"}\n confirmVariant={confirming.variant}\n processing={http.processing}\n onConfirm={() => void submit(confirming)}\n onCancel={() => setConfirming(null)}\n />\n )}\n\n {filling?.form && (\n <ActionForm\n cancelLabel={filling.confirmation?.cancelLabel ?? \"Cancel\"}\n componentRef={filling.ref}\n description={filling.confirmation?.description ?? undefined}\n endpoint={filling.endpoint}\n extraData={selectionPayload()}\n formNode={filling.form}\n method={filling.method}\n onClose={() => setFilling(null)}\n onSuccess={(response) => {\n dispatch(getActionEffects(response.effects));\n setFilling(null);\n onCompleted();\n }}\n submitLabel={filling.confirmation?.confirmLabel ?? filling.label}\n title={filling.confirmation?.title ?? filling.label}\n />\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAoBA,SAAgB,QAAQ,EACtB,SACA,cACA,aACA,OACA,OACA,sBACA,qBACA,eAUC;CACD,MAAM,EAAE,MAAM,KAAK,SAAS;CAC5B,MAAM,OAAO,QAAkC,CAAC,CAAC;CACjD,MAAM,WAAW,oBAAoB;CACrC,MAAM,CAAC,YAAY,iBAAiB,SAA4B,IAAI;CACpE,MAAM,CAAC,SAAS,cAAc,SAA4B,IAAI;CAE9D,MAAM,yBACJ,cAAc;EAAE,aAAa;EAAM,GAAG;CAAM,IAAI,EAAE,UAAU,aAAa;CAE3E,eAAe,OAAO,QAAmC;EACvD,KAAK,WAAW,UAAU;GAAE,GAAG;GAAM,GAAG,iBAAiB;EAAE,EAAE;EAO7D,IAAI,MALa,gBACT,KAAK,OAAO,QAAQ,OAAO,UAAU,EAAE,SAAS,YAAY,OAAO,OAAO,EAAE,EAAE,CAAC,GACrF,QACF,GAEQ;GACN,cAAc,IAAI;GAClB,YAAY;EACd;CACF;CAEA,SAAS,IAAI,QAA0B;EACrC,IAAI,OAAO,MAAM;GACf,WAAW,MAAM;GAEjB;EACF;EAEA,IAAI,OAAO,cAAc;GACvB,cAAc,MAAM;GAEpB;EACF;EAEA,OAAY,MAAM;CACpB;CAEA,MAAM,QAAQ,cAAe,SAAS,aAAa,SAAU,aAAa;CAE1E,OACE,qBAAC,OAAD;EAAK,WAAU;YAAf;GACE,oBAAC,QAAD;IAAM,WAAU;cACb,cACG,EAAE,0BAA0B,0BAA0B,EAAE,MAAM,CAAC,IAC/D,EAAE,uBAAuB,sBAAsB,EAAE,MAAM,CAAC;GACxD,CAAA;GACL,wBACC,oBAAC,UAAD;IACE,MAAK;IACL,aAAU;IACV,WAAU;IACV,SAAS;cAER,EAAE,gCAAgC,iCAAiC,EAAE,MAAM,CAAC;GACvE,CAAA;GAEV,oBAAC,OAAD;IAAK,WAAU;cACZ,QAAQ,KAAK,WACZ,qBAAC,QAAD;KAEE,MAAK;KACL,aAAW,eAAe,eAAe,OAAO,EAAE;KAClD,SAAS,OAAO;KAChB,UAAU,KAAK;KACf,eAAe,IAAI,MAAM;eAN3B,CAQG,KAAK,cAAc,oBAAC,SAAD,CAAU,CAAA,GAC7B,OAAO,KACF;OATD,OAAO,EASN,CACT;GACE,CAAA;GAEJ,YAAY,gBACX,oBAAC,eAAD;IACE,OAAO,WAAW,aAAa,SAAS,WAAW;IACnD,aAAa,WAAW,aAAa,eAAe,KAAA;IACpD,cAAc,WAAW,aAAa,gBAAgB,WAAW;IACjE,aAAa,WAAW,aAAa,eAAe;IACpD,gBAAgB,WAAW;IAC3B,YAAY,KAAK;IACjB,iBAAiB,KAAK,OAAO,UAAU;IACvC,gBAAgB,cAAc,IAAI;GACnC,CAAA;GAGF,SAAS,QACR,oBAAC,YAAD;IACE,aAAa,QAAQ,cAAc,eAAe;IAClD,cAAc,QAAQ;IACtB,aAAa,QAAQ,cAAc,eAAe,KAAA;IAClD,UAAU,QAAQ;IAClB,WAAW,iBAAiB;IAC5B,UAAU,QAAQ;IAClB,QAAQ,QAAQ;IAChB,eAAe,WAAW,IAAI;IAC9B,YAAY,aAAa;KACvB,SAAS,iBAAiB,SAAS,OAAO,CAAC;KAC3C,WAAW,IAAI;KACf,YAAY;IACd;IACA,aAAa,QAAQ,cAAc,gBAAgB,QAAQ;IAC3D,OAAO,QAAQ,cAAc,SAAS,QAAQ;GAC/C,CAAA;EAEA;;AAET"}
|
|
@@ -57,7 +57,7 @@ function ColumnFilterControl({ column, clauses, processing, onAdd, onUpdate, onR
|
|
|
57
57
|
asChild: true,
|
|
58
58
|
children: /* @__PURE__ */ jsxs("button", {
|
|
59
59
|
type: "button",
|
|
60
|
-
"aria-label": t("filter.columnFilters", "{{label}} filters", { label: column.label }),
|
|
60
|
+
"aria-label": t("table.filter.columnFilters", "{{label}} filters", { label: column.label }),
|
|
61
61
|
"data-test": `filter-${column.key}`,
|
|
62
62
|
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",
|
|
63
63
|
disabled: processing,
|
|
@@ -221,7 +221,7 @@ function FilterClauseList({ column, clauses, operators, defaultOperator, process
|
|
|
221
221
|
name: "plus",
|
|
222
222
|
"aria-hidden": "true",
|
|
223
223
|
className: "size-lt-icon-md"
|
|
224
|
-
}), t("filter.add", "Add filter")]
|
|
224
|
+
}), t("table.filter.add", "Add filter")]
|
|
225
225
|
})
|
|
226
226
|
})
|
|
227
227
|
]
|
|
@@ -235,7 +235,7 @@ function FilterClauseRow({ column, type, operators, clause, processing, onOperat
|
|
|
235
235
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
236
236
|
className: "flex items-center gap-2",
|
|
237
237
|
children: [operators.length > 1 ? /* @__PURE__ */ jsx("select", {
|
|
238
|
-
"aria-label": t("filter.operator", "{{label}} operator", { label: column.label }),
|
|
238
|
+
"aria-label": t("table.filter.operator", "{{label}} operator", { label: column.label }),
|
|
239
239
|
"data-test": `filter-${column.key}-operator`,
|
|
240
240
|
className: cn(fieldClass, "flex-1"),
|
|
241
241
|
disabled: processing,
|
|
@@ -250,7 +250,7 @@ function FilterClauseRow({ column, type, operators, clause, processing, onOperat
|
|
|
250
250
|
children: operatorLabel(clause.operator)
|
|
251
251
|
}), /* @__PURE__ */ jsx("button", {
|
|
252
252
|
type: "button",
|
|
253
|
-
"aria-label": t("filter.remove", "Remove {{label}} filter", { label: column.label }),
|
|
253
|
+
"aria-label": t("table.filter.remove", "Remove {{label}} filter", { label: column.label }),
|
|
254
254
|
"data-test": `filter-${column.key}-remove`,
|
|
255
255
|
className: "inline-flex size-9 items-center justify-center rounded-lt-sm border border-lt-border hover:bg-lt-muted disabled:opacity-50",
|
|
256
256
|
disabled: processing,
|
|
@@ -264,7 +264,7 @@ function FilterClauseRow({ column, type, operators, clause, processing, onOperat
|
|
|
264
264
|
}), !valueless && /* @__PURE__ */ jsx(FilterValueInput, {
|
|
265
265
|
type,
|
|
266
266
|
label: column.label,
|
|
267
|
-
ariaLabel: t("filter.value", "{{label}} filter value", { label: column.label }),
|
|
267
|
+
ariaLabel: t("table.filter.value", "{{label}} filter value", { label: column.label }),
|
|
268
268
|
value: clause.value,
|
|
269
269
|
processing,
|
|
270
270
|
onCommit: onValue,
|
|
@@ -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(\"filter.columnFilters\", \"{{label}} filters\", { label: column.label })}\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(\"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(\"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(\"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(\"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-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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"}
|
|
@@ -23,7 +23,7 @@ function FilterBar({ filters, values, processing, hasActiveFilters, onChange, on
|
|
|
23
23
|
"data-test": `table-filter-chip-${filter.key}-remove`,
|
|
24
24
|
className: "inline-flex size-5 items-center justify-center rounded-lt-sm text-lt-muted-fg hover:bg-lt-border disabled:opacity-50",
|
|
25
25
|
disabled: processing,
|
|
26
|
-
"aria-label": t("filter.remove", "Remove {{label}} filter", { label: filter.label }),
|
|
26
|
+
"aria-label": t("table.filter.remove", "Remove {{label}} filter", { label: filter.label }),
|
|
27
27
|
onClick: () => onChange(filter.key, void 0),
|
|
28
28
|
children: /* @__PURE__ */ jsx(Icon, {
|
|
29
29
|
name: "x",
|
|
@@ -37,7 +37,7 @@ function FilterBar({ filters, values, processing, hasActiveFilters, onChange, on
|
|
|
37
37
|
className: "text-lt-muted-fg underline-offset-2 hover:underline disabled:opacity-50",
|
|
38
38
|
disabled: processing,
|
|
39
39
|
onClick: onReset,
|
|
40
|
-
children: t("filter.resetAll", "Reset all")
|
|
40
|
+
children: t("table.filter.resetAll", "Reset all")
|
|
41
41
|
})]
|
|
42
42
|
})
|
|
43
43
|
});
|
|
@@ -49,7 +49,7 @@ function FilterMenu({ filters, values, processing, onChange, onSearch }) {
|
|
|
49
49
|
asChild: true,
|
|
50
50
|
children: /* @__PURE__ */ jsxs("button", {
|
|
51
51
|
type: "button",
|
|
52
|
-
"aria-label": t("filter.filters", "Filters"),
|
|
52
|
+
"aria-label": t("table.filter.filters", "Filters"),
|
|
53
53
|
"data-test": "table-filters-menu",
|
|
54
54
|
className: "relative inline-flex size-7 shrink-0 items-center justify-center rounded-lt-sm text-lt-muted-fg hover:bg-lt-muted hover:text-lt-fg disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-lt-ring/50 data-[state=open]:bg-lt-muted data-[state=open]:text-lt-fg",
|
|
55
55
|
disabled: processing,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filter-bar.js","names":[],"sources":["../../../resources/js/table/components/filter-bar.tsx"],"sourcesContent":["import { Icon } from \"@lattice-php/lattice/icons\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@lattice-php/lattice/core/components/popover\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport type { FilterData, Option } from \"@lattice-php/lattice/types/generated\";\nimport { filterOptions, isActiveFilterValue, stringProp } from \"../filter-values\";\nimport { TableFilterControl } from \"./filter-controls\";\n\nexport function FilterBar({\n filters,\n values,\n processing,\n hasActiveFilters,\n onChange,\n onReset,\n}: {\n filters: FilterData[];\n values: Record<string, unknown>;\n processing: boolean;\n hasActiveFilters: boolean;\n onChange: (key: string, value: unknown) => void;\n onReset: () => void;\n}) {\n const { t } = useT(\"lattice\");\n const active = filters.filter((filter) => isActiveFilterValue(values[filter.key]));\n\n if (!hasActiveFilters) {\n return null;\n }\n\n return (\n <div className=\"border-b border-lt-border px-4 py-3\">\n <div className=\"flex flex-wrap items-center gap-2 text-sm\">\n {active.map((filter) => (\n <span\n key={filter.key}\n className=\"inline-flex items-center gap-1.5 rounded-lt-sm bg-lt-muted px-2 py-1\"\n >\n <span>\n {`${filter.label}: `}\n <span className=\"font-semibold\">{displayValue(filter, values[filter.key])}</span>\n </span>\n <button\n type=\"button\"\n data-test={`table-filter-chip-${filter.key}-remove`}\n className=\"inline-flex size-5 items-center justify-center rounded-lt-sm text-lt-muted-fg hover:bg-lt-border disabled:opacity-50\"\n disabled={processing}\n aria-label={t(\"filter.remove\", \"Remove {{label}} filter\", {\n label: filter.label,\n })}\n onClick={() => onChange(filter.key, undefined)}\n >\n <Icon name=\"x\" aria-hidden=\"true\" className=\"size-lt-icon-sm\" />\n </button>\n </span>\n ))}\n <button\n type=\"button\"\n data-test=\"table-filters-reset\"\n className=\"text-lt-muted-fg underline-offset-2 hover:underline disabled:opacity-50\"\n disabled={processing}\n onClick={onReset}\n >\n {t(\"filter.resetAll\", \"Reset all\")}\n </button>\n </div>\n </div>\n );\n}\n\nexport function FilterMenu({\n filters,\n values,\n processing,\n onChange,\n onSearch,\n}: {\n filters: FilterData[];\n values: Record<string, unknown>;\n processing: boolean;\n onChange: (key: string, value: unknown) => void;\n onSearch?: (filterKey: string, query: string) => Promise<Option[]>;\n}) {\n const { t } = useT(\"lattice\");\n const active = filters.filter((filter) => isActiveFilterValue(values[filter.key]));\n const filtersLabel = t(\"filter.filters\", \"Filters\");\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n aria-label={filtersLabel}\n data-test=\"table-filters-menu\"\n className=\"relative inline-flex size-7 shrink-0 items-center justify-center rounded-lt-sm text-lt-muted-fg hover:bg-lt-muted hover:text-lt-fg disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-lt-ring/50 data-[state=open]:bg-lt-muted data-[state=open]:text-lt-fg\"\n disabled={processing}\n >\n <Icon name=\"filter\" aria-hidden=\"true\" className=\"size-lt-icon-md\" />\n {active.length > 0 && (\n <span className=\"absolute -right-1 -top-1 inline-flex size-3.5 items-center justify-center rounded-full bg-lt-primary text-[10px] font-medium leading-none text-lt-primary-fg\">\n {active.length}\n </span>\n )}\n </button>\n </PopoverTrigger>\n <PopoverContent align=\"end\" className=\"w-80 p-4\">\n <div className=\"grid gap-3\">\n {filters.map((filter) => (\n <div key={filter.key} className=\"grid gap-1\">\n {filter.type !== \"toggle\" && (\n <span className=\"text-xs font-medium text-lt-muted-fg\">{filter.label}</span>\n )}\n <TableFilterControl\n filter={filter}\n value={values[filter.key]}\n processing={processing}\n onChange={(value) => onChange(filter.key, value)}\n onSearch={onSearch ? (query) => onSearch(filter.key, query) : undefined}\n />\n </div>\n ))}\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n\nfunction optionLabel(filter: FilterData, value: string): string {\n return filterOptions(filter).find((option) => option.value === value)?.label ?? value;\n}\n\nfunction displayValue(filter: FilterData, value: unknown): string {\n if (filter.type === \"select\") {\n if (Array.isArray(value)) {\n return value.map((item) => optionLabel(filter, String(item))).join(\", \");\n }\n\n return optionLabel(filter, String(value));\n }\n\n if (filter.type === \"ternary\") {\n return value === \"true\"\n ? stringProp(filter, \"trueLabel\", \"True\")\n : stringProp(filter, \"falseLabel\", \"False\");\n }\n\n if (filter.type === \"date-range\" && value && typeof value === \"object\") {\n const range = value as { from?: string; until?: string };\n\n return [range.from, range.until].filter((part) => part).join(\" – \");\n }\n\n return String(value);\n}\n"],"mappings":";;;;;;;AAWA,SAAgB,UAAU,EACxB,SACA,QACA,YACA,kBACA,UACA,WAQC;CACD,MAAM,EAAE,MAAM,KAAK,SAAS;CAC5B,MAAM,SAAS,QAAQ,QAAQ,WAAW,oBAAoB,OAAO,OAAO,IAAI,CAAC;CAEjF,IAAI,CAAC,kBACH,OAAO;CAGT,OACE,oBAAC,OAAD;EAAK,WAAU;YACb,qBAAC,OAAD;GAAK,WAAU;aAAf,CACG,OAAO,KAAK,WACX,qBAAC,QAAD;IAEE,WAAU;cAFZ,CAIE,qBAAC,QAAD,EAAA,UAAA,CACG,GAAG,OAAO,MAAM,KACjB,oBAAC,QAAD;KAAM,WAAU;eAAiB,aAAa,QAAQ,OAAO,OAAO,IAAI;IAAQ,CAAA,CAC5E,EAAA,CAAA,GACN,oBAAC,UAAD;KACE,MAAK;KACL,aAAW,qBAAqB,OAAO,IAAI;KAC3C,WAAU;KACV,UAAU;KACV,cAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"filter-bar.js","names":[],"sources":["../../../resources/js/table/components/filter-bar.tsx"],"sourcesContent":["import { Icon } from \"@lattice-php/lattice/icons\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@lattice-php/lattice/core/components/popover\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport type { FilterData, Option } from \"@lattice-php/lattice/types/generated\";\nimport { filterOptions, isActiveFilterValue, stringProp } from \"../filter-values\";\nimport { TableFilterControl } from \"./filter-controls\";\n\nexport function FilterBar({\n filters,\n values,\n processing,\n hasActiveFilters,\n onChange,\n onReset,\n}: {\n filters: FilterData[];\n values: Record<string, unknown>;\n processing: boolean;\n hasActiveFilters: boolean;\n onChange: (key: string, value: unknown) => void;\n onReset: () => void;\n}) {\n const { t } = useT(\"lattice\");\n const active = filters.filter((filter) => isActiveFilterValue(values[filter.key]));\n\n if (!hasActiveFilters) {\n return null;\n }\n\n return (\n <div className=\"border-b border-lt-border px-4 py-3\">\n <div className=\"flex flex-wrap items-center gap-2 text-sm\">\n {active.map((filter) => (\n <span\n key={filter.key}\n className=\"inline-flex items-center gap-1.5 rounded-lt-sm bg-lt-muted px-2 py-1\"\n >\n <span>\n {`${filter.label}: `}\n <span className=\"font-semibold\">{displayValue(filter, values[filter.key])}</span>\n </span>\n <button\n type=\"button\"\n data-test={`table-filter-chip-${filter.key}-remove`}\n className=\"inline-flex size-5 items-center justify-center rounded-lt-sm text-lt-muted-fg hover:bg-lt-border disabled:opacity-50\"\n disabled={processing}\n aria-label={t(\"table.filter.remove\", \"Remove {{label}} filter\", {\n label: filter.label,\n })}\n onClick={() => onChange(filter.key, undefined)}\n >\n <Icon name=\"x\" aria-hidden=\"true\" className=\"size-lt-icon-sm\" />\n </button>\n </span>\n ))}\n <button\n type=\"button\"\n data-test=\"table-filters-reset\"\n className=\"text-lt-muted-fg underline-offset-2 hover:underline disabled:opacity-50\"\n disabled={processing}\n onClick={onReset}\n >\n {t(\"table.filter.resetAll\", \"Reset all\")}\n </button>\n </div>\n </div>\n );\n}\n\nexport function FilterMenu({\n filters,\n values,\n processing,\n onChange,\n onSearch,\n}: {\n filters: FilterData[];\n values: Record<string, unknown>;\n processing: boolean;\n onChange: (key: string, value: unknown) => void;\n onSearch?: (filterKey: string, query: string) => Promise<Option[]>;\n}) {\n const { t } = useT(\"lattice\");\n const active = filters.filter((filter) => isActiveFilterValue(values[filter.key]));\n const filtersLabel = t(\"table.filter.filters\", \"Filters\");\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n aria-label={filtersLabel}\n data-test=\"table-filters-menu\"\n className=\"relative inline-flex size-7 shrink-0 items-center justify-center rounded-lt-sm text-lt-muted-fg hover:bg-lt-muted hover:text-lt-fg disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-lt-ring/50 data-[state=open]:bg-lt-muted data-[state=open]:text-lt-fg\"\n disabled={processing}\n >\n <Icon name=\"filter\" aria-hidden=\"true\" className=\"size-lt-icon-md\" />\n {active.length > 0 && (\n <span className=\"absolute -right-1 -top-1 inline-flex size-3.5 items-center justify-center rounded-full bg-lt-primary text-[10px] font-medium leading-none text-lt-primary-fg\">\n {active.length}\n </span>\n )}\n </button>\n </PopoverTrigger>\n <PopoverContent align=\"end\" className=\"w-80 p-4\">\n <div className=\"grid gap-3\">\n {filters.map((filter) => (\n <div key={filter.key} className=\"grid gap-1\">\n {filter.type !== \"toggle\" && (\n <span className=\"text-xs font-medium text-lt-muted-fg\">{filter.label}</span>\n )}\n <TableFilterControl\n filter={filter}\n value={values[filter.key]}\n processing={processing}\n onChange={(value) => onChange(filter.key, value)}\n onSearch={onSearch ? (query) => onSearch(filter.key, query) : undefined}\n />\n </div>\n ))}\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n\nfunction optionLabel(filter: FilterData, value: string): string {\n return filterOptions(filter).find((option) => option.value === value)?.label ?? value;\n}\n\nfunction displayValue(filter: FilterData, value: unknown): string {\n if (filter.type === \"select\") {\n if (Array.isArray(value)) {\n return value.map((item) => optionLabel(filter, String(item))).join(\", \");\n }\n\n return optionLabel(filter, String(value));\n }\n\n if (filter.type === \"ternary\") {\n return value === \"true\"\n ? stringProp(filter, \"trueLabel\", \"True\")\n : stringProp(filter, \"falseLabel\", \"False\");\n }\n\n if (filter.type === \"date-range\" && value && typeof value === \"object\") {\n const range = value as { from?: string; until?: string };\n\n return [range.from, range.until].filter((part) => part).join(\" – \");\n }\n\n return String(value);\n}\n"],"mappings":"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"}
|
|
@@ -57,7 +57,7 @@ function TableFilterControl({ filter, value, processing, onChange, onSearch }) {
|
|
|
57
57
|
}
|
|
58
58
|
function SelectControl({ filter, value, processing, onChange }) {
|
|
59
59
|
const { t } = useT("lattice");
|
|
60
|
-
const placeholder = stringProp(filter, "placeholder", t("filter.all", "All"));
|
|
60
|
+
const placeholder = stringProp(filter, "placeholder", t("table.filter.all", "All"));
|
|
61
61
|
return /* @__PURE__ */ jsxs("select", {
|
|
62
62
|
"aria-label": filter.label,
|
|
63
63
|
"data-test": `table-filter-${filter.key}`,
|
|
@@ -81,7 +81,7 @@ function MultiSelectControl({ filter, value, processing, onChange }) {
|
|
|
81
81
|
function toggle(optionValue) {
|
|
82
82
|
onChange(selected.includes(optionValue) ? selected.filter((item) => item !== optionValue) : [...selected, optionValue]);
|
|
83
83
|
}
|
|
84
|
-
const summary = selected.length === 0 ? stringProp(filter, "placeholder", t("filter.all", "All")) : t("filter.selectedCount", "{{amount}} selected", { amount: selected.length });
|
|
84
|
+
const summary = selected.length === 0 ? stringProp(filter, "placeholder", t("table.filter.all", "All")) : t("table.filter.selectedCount", "{{amount}} selected", { amount: selected.length });
|
|
85
85
|
return /* @__PURE__ */ jsxs("div", {
|
|
86
86
|
className: "relative",
|
|
87
87
|
children: [/* @__PURE__ */ jsxs("button", {
|
|
@@ -126,7 +126,7 @@ function SearchableSelectControl({ filter, value, processing, onChange, onSearch
|
|
|
126
126
|
const [loading, setLoading] = useState(false);
|
|
127
127
|
const selected = multiple ? Array.isArray(value) ? value : [] : typeof value === "string" && value !== "" ? [value] : [];
|
|
128
128
|
const labels = new Map([...filterOptions(filter), ...results].map((option) => [option.value, option.label]));
|
|
129
|
-
const summary = selected.length === 0 ? stringProp(filter, "placeholder", t("filter.all", "All")) : multiple ? t("filter.selectedCount", "{{amount}} selected", { amount: selected.length }) : labels.get(selected[0]) ?? selected[0];
|
|
129
|
+
const summary = selected.length === 0 ? stringProp(filter, "placeholder", t("table.filter.all", "All")) : multiple ? t("table.filter.selectedCount", "{{amount}} selected", { amount: selected.length }) : labels.get(selected[0]) ?? selected[0];
|
|
130
130
|
function search(query) {
|
|
131
131
|
setLoading(true);
|
|
132
132
|
onSearch(query).then((options) => {
|
|
@@ -146,7 +146,7 @@ function SearchableSelectControl({ filter, value, processing, onChange, onSearch
|
|
|
146
146
|
open,
|
|
147
147
|
onOpenChange: setOpen,
|
|
148
148
|
options: results,
|
|
149
|
-
searchLabel: t("filter.search", "Search"),
|
|
149
|
+
searchLabel: t("table.filter.search", "Search"),
|
|
150
150
|
selected,
|
|
151
151
|
testId: `table-filter-${filter.key}`,
|
|
152
152
|
trigger: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
@@ -167,9 +167,9 @@ function SearchableSelectControl({ filter, value, processing, onChange, onSearch
|
|
|
167
167
|
}
|
|
168
168
|
function TernaryControl({ filter, value, processing, onChange }) {
|
|
169
169
|
const { t } = useT("lattice");
|
|
170
|
-
const placeholder = stringProp(filter, "placeholder", t("filter.all", "All"));
|
|
171
|
-
const trueLabel = stringProp(filter, "trueLabel", t("filter.true", "True"));
|
|
172
|
-
const falseLabel = stringProp(filter, "falseLabel", t("filter.false", "False"));
|
|
170
|
+
const placeholder = stringProp(filter, "placeholder", t("table.filter.all", "All"));
|
|
171
|
+
const trueLabel = stringProp(filter, "trueLabel", t("table.filter.true", "True"));
|
|
172
|
+
const falseLabel = stringProp(filter, "falseLabel", t("table.filter.false", "False"));
|
|
173
173
|
return /* @__PURE__ */ jsxs("select", {
|
|
174
174
|
"aria-label": filter.label,
|
|
175
175
|
"data-test": `table-filter-${filter.key}`,
|
|
@@ -207,7 +207,7 @@ function DateRangeControl({ filter, value, processing, onChange }) {
|
|
|
207
207
|
children: [
|
|
208
208
|
/* @__PURE__ */ jsx("input", {
|
|
209
209
|
type: "date",
|
|
210
|
-
"aria-label": t("filter.from", "{{label}} from", { label: filter.label }),
|
|
210
|
+
"aria-label": t("table.filter.from", "{{label}} from", { label: filter.label }),
|
|
211
211
|
"data-test": `table-filter-${filter.key}-from`,
|
|
212
212
|
className: fieldClass,
|
|
213
213
|
disabled: processing,
|
|
@@ -224,7 +224,7 @@ function DateRangeControl({ filter, value, processing, onChange }) {
|
|
|
224
224
|
}),
|
|
225
225
|
/* @__PURE__ */ jsx("input", {
|
|
226
226
|
type: "date",
|
|
227
|
-
"aria-label": t("filter.until", "{{label}} until", { label: filter.label }),
|
|
227
|
+
"aria-label": t("table.filter.until", "{{label}} until", { label: filter.label }),
|
|
228
228
|
"data-test": `table-filter-${filter.key}-until`,
|
|
229
229
|
className: fieldClass,
|
|
230
230
|
disabled: processing,
|
|
@@ -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(\"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(\"filter.all\", \"All\"))\n : t(\"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(\"filter.all\", \"All\"))\n : multiple\n ? t(\"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(\"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(\"filter.all\", \"All\"));\n const trueLabel = stringProp(filter, \"trueLabel\", t(\"filter.true\", \"True\"));\n const falseLabel = stringProp(filter, \"falseLabel\", t(\"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(\"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(\"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-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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"}
|
|
@@ -20,7 +20,7 @@ function FilterStackBar({ filters, columnsByKey, processing, onRemove }) {
|
|
|
20
20
|
"data-test": `filter-chip-${clause.field}-remove`,
|
|
21
21
|
className: "inline-flex size-5 items-center justify-center rounded-lt-sm text-lt-muted-fg hover:bg-lt-muted disabled:opacity-50",
|
|
22
22
|
disabled: processing,
|
|
23
|
-
"aria-label": t("filter.remove", "Remove {{label}} filter", { label }),
|
|
23
|
+
"aria-label": t("table.filter.remove", "Remove {{label}} filter", { label }),
|
|
24
24
|
onClick: () => onRemove(index),
|
|
25
25
|
children: /* @__PURE__ */ jsx(Icon, {
|
|
26
26
|
name: "x",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filter-stack-bar.js","names":[],"sources":["../../../resources/js/table/components/filter-stack-bar.tsx"],"sourcesContent":["import { Icon } from \"@lattice-php/lattice/icons\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { operatorLabel, VALUELESS_FILTER_OPERATORS } from \"../query\";\nimport type { FilterClause, TableColumn } from \"../types\";\n\nexport function FilterStackBar({\n filters,\n columnsByKey,\n processing,\n onRemove,\n}: {\n filters: FilterClause[];\n columnsByKey: Map<string, TableColumn>;\n processing: boolean;\n onRemove: (index: number) => void;\n}) {\n const { t } = useT(\"lattice\");\n\n return (\n <div className=\"flex flex-wrap items-center gap-4 border-b border-lt-border px-4 py-2.5 text-sm\">\n {filters.map((clause, index) => {\n const label = columnsByKey.get(clause.field)?.label ?? clause.field;\n const valueless = VALUELESS_FILTER_OPERATORS.has(clause.operator);\n\n return (\n <span\n key={`${clause.field}-${clause.operator}-${index}`}\n className=\"inline-flex items-center gap-1.5\"\n >\n <span>\n {valueless\n ? `${label} ${operatorLabel(clause.operator)}`\n : `${label} ${operatorLabel(clause.operator)}: `}\n {!valueless && <span className=\"font-semibold\">{clause.value}</span>}\n </span>\n <button\n type=\"button\"\n data-test={`filter-chip-${clause.field}-remove`}\n className=\"inline-flex size-5 items-center justify-center rounded-lt-sm text-lt-muted-fg hover:bg-lt-muted disabled:opacity-50\"\n disabled={processing}\n aria-label={t(\"filter.remove\", \"Remove {{label}} filter\", { label })}\n onClick={() => onRemove(index)}\n >\n <Icon name=\"x\" aria-hidden=\"true\" className=\"size-lt-icon-sm\" />\n </button>\n </span>\n );\n })}\n </div>\n );\n}\n"],"mappings":";;;;;AAKA,SAAgB,eAAe,EAC7B,SACA,cACA,YACA,YAMC;CACD,MAAM,EAAE,MAAM,KAAK,SAAS;CAE5B,OACE,oBAAC,OAAD;EAAK,WAAU;YACZ,QAAQ,KAAK,QAAQ,UAAU;GAC9B,MAAM,QAAQ,aAAa,IAAI,OAAO,KAAK,GAAG,SAAS,OAAO;GAC9D,MAAM,YAAY,2BAA2B,IAAI,OAAO,QAAQ;GAEhE,OACE,qBAAC,QAAD;IAEE,WAAU;cAFZ,CAIE,qBAAC,QAAD,EAAA,UAAA,CACG,YACG,GAAG,MAAM,GAAG,cAAc,OAAO,QAAQ,MACzC,GAAG,MAAM,GAAG,cAAc,OAAO,QAAQ,EAAE,KAC9C,CAAC,aAAa,oBAAC,QAAD;KAAM,WAAU;eAAiB,OAAO;IAAY,CAAA,CAC/D,EAAA,CAAA,GACN,oBAAC,UAAD;KACE,MAAK;KACL,aAAW,eAAe,OAAO,MAAM;KACvC,WAAU;KACV,UAAU;KACV,cAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"filter-stack-bar.js","names":[],"sources":["../../../resources/js/table/components/filter-stack-bar.tsx"],"sourcesContent":["import { Icon } from \"@lattice-php/lattice/icons\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport { operatorLabel, VALUELESS_FILTER_OPERATORS } from \"../query\";\nimport type { FilterClause, TableColumn } from \"../types\";\n\nexport function FilterStackBar({\n filters,\n columnsByKey,\n processing,\n onRemove,\n}: {\n filters: FilterClause[];\n columnsByKey: Map<string, TableColumn>;\n processing: boolean;\n onRemove: (index: number) => void;\n}) {\n const { t } = useT(\"lattice\");\n\n return (\n <div className=\"flex flex-wrap items-center gap-4 border-b border-lt-border px-4 py-2.5 text-sm\">\n {filters.map((clause, index) => {\n const label = columnsByKey.get(clause.field)?.label ?? clause.field;\n const valueless = VALUELESS_FILTER_OPERATORS.has(clause.operator);\n\n return (\n <span\n key={`${clause.field}-${clause.operator}-${index}`}\n className=\"inline-flex items-center gap-1.5\"\n >\n <span>\n {valueless\n ? `${label} ${operatorLabel(clause.operator)}`\n : `${label} ${operatorLabel(clause.operator)}: `}\n {!valueless && <span className=\"font-semibold\">{clause.value}</span>}\n </span>\n <button\n type=\"button\"\n data-test={`filter-chip-${clause.field}-remove`}\n className=\"inline-flex size-5 items-center justify-center rounded-lt-sm text-lt-muted-fg hover:bg-lt-muted disabled:opacity-50\"\n disabled={processing}\n aria-label={t(\"table.filter.remove\", \"Remove {{label}} filter\", { label })}\n onClick={() => onRemove(index)}\n >\n <Icon name=\"x\" aria-hidden=\"true\" className=\"size-lt-icon-sm\" />\n </button>\n </span>\n );\n })}\n </div>\n );\n}\n"],"mappings":";;;;;AAKA,SAAgB,eAAe,EAC7B,SACA,cACA,YACA,YAMC;CACD,MAAM,EAAE,MAAM,KAAK,SAAS;CAE5B,OACE,oBAAC,OAAD;EAAK,WAAU;YACZ,QAAQ,KAAK,QAAQ,UAAU;GAC9B,MAAM,QAAQ,aAAa,IAAI,OAAO,KAAK,GAAG,SAAS,OAAO;GAC9D,MAAM,YAAY,2BAA2B,IAAI,OAAO,QAAQ;GAEhE,OACE,qBAAC,QAAD;IAEE,WAAU;cAFZ,CAIE,qBAAC,QAAD,EAAA,UAAA,CACG,YACG,GAAG,MAAM,GAAG,cAAc,OAAO,QAAQ,MACzC,GAAG,MAAM,GAAG,cAAc,OAAO,QAAQ,EAAE,KAC9C,CAAC,aAAa,oBAAC,QAAD;KAAM,WAAU;eAAiB,OAAO;IAAY,CAAA,CAC/D,EAAA,CAAA,GACN,oBAAC,UAAD;KACE,MAAK;KACL,aAAW,eAAe,OAAO,MAAM;KACvC,WAAU;KACV,UAAU;KACV,cAAY,EAAE,uBAAuB,2BAA2B,EAAE,MAAM,CAAC;KACzE,eAAe,SAAS,KAAK;eAE7B,oBAAC,MAAD;MAAM,MAAK;MAAI,eAAY;MAAO,WAAU;KAAmB,CAAA;IACzD,CAAA,CACJ;MAnBC,GAAG,OAAO,MAAM,GAAG,OAAO,SAAS,GAAG,OAmBvC;EAEV,CAAC;CACE,CAAA;AAET"}
|
|
@@ -8,7 +8,7 @@ var fieldClass = controlSurface({ density: "compact" });
|
|
|
8
8
|
function FilterValueInput({ type, label, value, processing, withSearchIcon = false, grouped = false, ariaLabel, testId, onCommit, onClear }) {
|
|
9
9
|
const { t } = useT("lattice");
|
|
10
10
|
const [draft, setDraft] = useState(value);
|
|
11
|
-
const inputLabel = ariaLabel ?? t("filter.filterBy", "Filter {{label}}", { label });
|
|
11
|
+
const inputLabel = ariaLabel ?? t("table.filter.filterBy", "Filter {{label}}", { label });
|
|
12
12
|
const groupedClass = grouped ? "rounded-r-none" : "";
|
|
13
13
|
useEffect(() => {
|
|
14
14
|
setDraft(value);
|
|
@@ -23,15 +23,15 @@ function FilterValueInput({ type, label, value, processing, withSearchIcon = fal
|
|
|
23
23
|
children: [
|
|
24
24
|
/* @__PURE__ */ jsx("option", {
|
|
25
25
|
value: "",
|
|
26
|
-
children: t("filter.all", "All")
|
|
26
|
+
children: t("table.filter.all", "All")
|
|
27
27
|
}),
|
|
28
28
|
/* @__PURE__ */ jsx("option", {
|
|
29
29
|
value: "true",
|
|
30
|
-
children: t("filter.true", "True")
|
|
30
|
+
children: t("table.filter.true", "True")
|
|
31
31
|
}),
|
|
32
32
|
/* @__PURE__ */ jsx("option", {
|
|
33
33
|
value: "false",
|
|
34
|
-
children: t("filter.false", "False")
|
|
34
|
+
children: t("table.filter.false", "False")
|
|
35
35
|
})
|
|
36
36
|
]
|
|
37
37
|
});
|
|
@@ -72,7 +72,7 @@ function FilterValueInput({ type, label, value, processing, withSearchIcon = fal
|
|
|
72
72
|
}),
|
|
73
73
|
onClear && draft !== "" && /* @__PURE__ */ jsx("button", {
|
|
74
74
|
type: "button",
|
|
75
|
-
"aria-label": t("filter.clear", "Clear {{label}} filter", { label }),
|
|
75
|
+
"aria-label": t("table.filter.clear", "Clear {{label}} filter", { label }),
|
|
76
76
|
"data-test": testId ? `${testId}-clear` : void 0,
|
|
77
77
|
className: "absolute right-1 inline-flex size-6 items-center justify-center rounded-lt-sm hover:bg-lt-muted disabled:opacity-50",
|
|
78
78
|
disabled: processing,
|