@plumile/backoffice-react 0.1.205 → 0.1.206
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/README.md +7 -0
- package/lib/esm/components/backoffice/pickers/EntityIdPickerDialog.js +123 -70
- package/lib/esm/components/backoffice/pickers/EntityIdPickerDialog.js.map +1 -1
- package/lib/esm/components/backoffice/scaffolds/BackofficeEntityListScaffold.js +151 -149
- package/lib/esm/components/backoffice/scaffolds/BackofficeEntityListScaffold.js.map +1 -1
- package/lib/esm/hooks/useInfiniteConnection.js +40 -38
- package/lib/esm/hooks/useInfiniteConnection.js.map +1 -1
- package/lib/esm/i18n/locales/en/backofficeReact.js +1 -0
- package/lib/esm/i18n/locales/en/backofficeReact.js.map +1 -1
- package/lib/esm/i18n/locales/fr/backofficeReact.js +1 -0
- package/lib/esm/i18n/locales/fr/backofficeReact.js.map +1 -1
- package/lib/esm/index.js +42 -42
- package/lib/esm/pages/BackofficeEntityListDataPage.js +39 -31
- package/lib/esm/pages/BackofficeEntityListDataPage.js.map +1 -1
- package/lib/esm/pages/BackofficeEntityListPage.js +2 -0
- package/lib/esm/pages/BackofficeEntityListPage.js.map +1 -1
- package/lib/types/components/backoffice/pickers/EntityIdPickerDialog.d.ts.map +1 -1
- package/lib/types/components/backoffice/scaffolds/BackofficeEntityListScaffold.d.ts +2 -0
- package/lib/types/components/backoffice/scaffolds/BackofficeEntityListScaffold.d.ts.map +1 -1
- package/lib/types/hooks/useInfiniteConnection.d.ts +3 -2
- package/lib/types/hooks/useInfiniteConnection.d.ts.map +1 -1
- package/lib/types/i18n/resources.d.ts +2 -0
- package/lib/types/i18n/resources.d.ts.map +1 -1
- package/lib/types/pages/BackofficeEntityListDataPage.d.ts.map +1 -1
- package/lib/types/pages/BackofficeEntityListPage.d.ts.map +1 -1
- package/package.json +6 -6
- package/lib/esm/hooks/useBackofficeLoadMore.js +0 -18
- package/lib/esm/hooks/useBackofficeLoadMore.js.map +0 -1
- package/lib/types/hooks/useBackofficeLoadMore.d.ts +0 -9
- package/lib/types/hooks/useBackofficeLoadMore.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -91,6 +91,11 @@ without loading the list facet.
|
|
|
91
91
|
|
|
92
92
|
### Relay and mutation helpers
|
|
93
93
|
|
|
94
|
+
- `useInfiniteConnection` is the required connection lifecycle for generic and
|
|
95
|
+
application lists. Callers provide a telemetry `surface`, a variable-derived
|
|
96
|
+
`resetKey`, and adapt Relay `loadNext(count, { onComplete })` through
|
|
97
|
+
`runLoadMore`; the hook owns deduplication, timeout, incremental error, and
|
|
98
|
+
retry behavior.
|
|
94
99
|
- `resolveMutationOutcome`
|
|
95
100
|
- `resolveAgentStartOutcome`
|
|
96
101
|
- `requireField`
|
|
@@ -112,6 +117,8 @@ only helpers tied to backoffice mutations, connections, and typed data.
|
|
|
112
117
|
`BackofficeLifecycleTimelineSection` and `BackofficeEntityLink`
|
|
113
118
|
- `EntityIdPickerDialog`, which remains the backoffice picker integration for
|
|
114
119
|
manifests, picker facets, Relay loading, scope, search, and selected IDs.
|
|
120
|
+
It debounces search, paginates the manifest connection, and observes the
|
|
121
|
+
modal content scroll root while preserving local incremental retry.
|
|
115
122
|
The reusable picker visuals live in `@plumile/ui` as
|
|
116
123
|
`BackofficePickerShell`, `BackofficePickerList`, and `BackofficePickerRow`.
|
|
117
124
|
|
|
@@ -1,126 +1,179 @@
|
|
|
1
1
|
import { useBackofficeReactTranslation as e } from "../../../i18n/useBackofficeReactTranslation.js";
|
|
2
2
|
import { BackofficeErrorBoundary as t } from "../errors/BackofficeErrorBoundary.js";
|
|
3
3
|
import { useBackofficePickerEntityLoader as n } from "../../../provider/useBackofficeEntityLoader.js";
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
4
|
+
import r from "../../../hooks/useInfiniteConnection.js";
|
|
5
|
+
import { useBackofficeRuntimePaginationFragment as i } from "../../../relay/typedRelayHooks.js";
|
|
6
|
+
import { loadingLabel as a, loadingState as o } from "./entityIdPickerDialog.css.js";
|
|
7
|
+
import { Suspense as s, useCallback as c, useEffect as l, useMemo as u, useRef as d, useState as f } from "react";
|
|
8
|
+
import { InlineBanner as p } from "@plumile/ui/components/feedback/InlineBanner.js";
|
|
9
|
+
import { Fragment as m, jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
10
|
+
import { useTranslation as _ } from "react-i18next";
|
|
11
|
+
import { Button as v } from "@plumile/ui/atomic/atoms/button/Button.js";
|
|
12
|
+
import * as y from "react-relay";
|
|
13
|
+
import { Modal as b } from "@plumile/ui/atomic/atoms/modal/Modal.js";
|
|
14
|
+
import { Spinner as x } from "@plumile/ui/backoffice/atoms/spinner/Spinner.js";
|
|
15
|
+
import { BackofficeEmptyState as S } from "@plumile/ui/backoffice/molecules/backoffice_empty_state/BackofficeEmptyState.js";
|
|
16
|
+
import { BackofficePickerList as C, BackofficePickerShell as w } from "@plumile/ui/backoffice/molecules/backoffice_picker/BackofficePicker.js";
|
|
17
|
+
import { InfiniteScrollTrigger as T } from "@plumile/ui/components/infinite/InfiniteScrollTrigger.js";
|
|
16
18
|
//#region src/components/backoffice/pickers/EntityIdPickerDialog.tsx
|
|
17
|
-
var { useLazyLoadQuery:
|
|
18
|
-
let
|
|
19
|
-
let
|
|
19
|
+
var { useLazyLoadQuery: E } = y, D = "store-and-network", O = (e, t) => e(t), k = ({ config: e, entity: t, search: n, scope: a, fetchKey: o, resetKey: s, scrollRootRef: l, labels: d, onSelectId: f }) => {
|
|
20
|
+
let _ = u(() => {
|
|
21
|
+
let t = n.trim();
|
|
20
22
|
return e.buildQueryVariables({
|
|
21
|
-
search:
|
|
22
|
-
scope:
|
|
23
|
+
search: t,
|
|
24
|
+
scope: a
|
|
23
25
|
});
|
|
24
26
|
}, [
|
|
25
27
|
e,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
]),
|
|
29
|
-
fetchPolicy:
|
|
30
|
-
fetchKey:
|
|
31
|
-
}),
|
|
28
|
+
a,
|
|
29
|
+
n
|
|
30
|
+
]), y = E(e.query, _, {
|
|
31
|
+
fetchPolicy: D,
|
|
32
|
+
fetchKey: o
|
|
33
|
+
}), { data: b, loadNext: x, hasNext: S, isLoadingNext: w } = i(e.fragment, y), O = e.getConnection(b), k = u(() => O.edges.map((t) => {
|
|
32
34
|
let n = e.toRow(t.node);
|
|
33
35
|
return {
|
|
34
36
|
id: e.getRowId(n),
|
|
35
37
|
title: n.title,
|
|
36
38
|
subtitle: n.subtitle
|
|
37
39
|
};
|
|
38
|
-
}), [e,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
}), [e, O.edges]), A = c((e) => {
|
|
41
|
+
x(20, { onComplete: e });
|
|
42
|
+
}, [x]), j = r({
|
|
43
|
+
nodes: k,
|
|
44
|
+
hasNext: S,
|
|
45
|
+
isLoadingNext: w,
|
|
46
|
+
runLoadMore: A,
|
|
47
|
+
surface: `backoffice.entity-picker.${t}`,
|
|
48
|
+
resetKey: s
|
|
44
49
|
});
|
|
45
|
-
|
|
50
|
+
return /* @__PURE__ */ g(m, { children: [
|
|
51
|
+
/* @__PURE__ */ h(C, {
|
|
52
|
+
items: j.nodes,
|
|
53
|
+
onSelectItem: (e) => {
|
|
54
|
+
f(e.id);
|
|
55
|
+
}
|
|
56
|
+
}),
|
|
57
|
+
/* @__PURE__ */ h(T, {
|
|
58
|
+
disabled: j.incrementalError != null,
|
|
59
|
+
hasNext: j.hasNext,
|
|
60
|
+
isLoading: j.isLoadingNext,
|
|
61
|
+
onLoadMore: j.loadMoreSafe,
|
|
62
|
+
scrollRootRef: l,
|
|
63
|
+
labels: {
|
|
64
|
+
loadMore: d.loadMore,
|
|
65
|
+
loading: d.loading,
|
|
66
|
+
end: d.end
|
|
67
|
+
}
|
|
68
|
+
}),
|
|
69
|
+
j.incrementalError != null && /* @__PURE__ */ h(p, {
|
|
70
|
+
tone: "danger",
|
|
71
|
+
title: j.incrementalError.message,
|
|
72
|
+
actions: /* @__PURE__ */ h(v, {
|
|
73
|
+
type: "button",
|
|
74
|
+
variant: "secondary",
|
|
75
|
+
onClick: j.retryLoadMore,
|
|
76
|
+
children: d.retry
|
|
77
|
+
}),
|
|
78
|
+
children: null
|
|
79
|
+
})
|
|
80
|
+
] });
|
|
81
|
+
}, A = (e, t) => /* @__PURE__ */ h(m, { children: /* @__PURE__ */ h(v, {
|
|
46
82
|
type: "button",
|
|
47
83
|
variant: "secondary",
|
|
48
84
|
onClick: e,
|
|
49
85
|
children: t
|
|
50
|
-
}) }),
|
|
86
|
+
}) }), j = ({ message: e }) => /* @__PURE__ */ h(p, {
|
|
51
87
|
tone: "warning",
|
|
52
88
|
title: e,
|
|
53
89
|
children: null
|
|
54
|
-
}),
|
|
55
|
-
className:
|
|
90
|
+
}), M = ({ label: e }) => /* @__PURE__ */ g("div", {
|
|
91
|
+
className: o,
|
|
56
92
|
role: "status",
|
|
57
93
|
"aria-live": "polite",
|
|
58
|
-
children: [/* @__PURE__ */
|
|
94
|
+
children: [/* @__PURE__ */ h(x, {
|
|
59
95
|
size: 18,
|
|
60
96
|
ariaLabel: e
|
|
61
|
-
}), /* @__PURE__ */
|
|
62
|
-
className:
|
|
97
|
+
}), /* @__PURE__ */ h("span", {
|
|
98
|
+
className: a,
|
|
63
99
|
children: e
|
|
64
100
|
})]
|
|
65
|
-
}),
|
|
66
|
-
let { t: y } =
|
|
67
|
-
|
|
68
|
-
r && (
|
|
69
|
-
}, [r])
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
101
|
+
}), N = ({ isOpen: r, entity: i, title: a, scope: o, onClose: m, onSelectId: g }) => {
|
|
102
|
+
let { t: y } = _(), { t: x } = e(), [C, T] = f(""), [E, D] = f(""), [N, P] = f(0), F = d(null), I = n(i, { enabled: r }), L;
|
|
103
|
+
I.status === "loaded" && (L = I.module.config.picker), l(() => {
|
|
104
|
+
r && (T(""), D(""), P((e) => e + 1));
|
|
105
|
+
}, [r]), l(() => {
|
|
106
|
+
let e = setTimeout(() => {
|
|
107
|
+
D(C);
|
|
108
|
+
}, 250);
|
|
109
|
+
return () => {
|
|
110
|
+
clearTimeout(e);
|
|
111
|
+
};
|
|
112
|
+
}, [C]);
|
|
113
|
+
let R = u(() => A(m, x("common.actions.close")), [m, x]), z = c((e) => {
|
|
114
|
+
T(e);
|
|
115
|
+
}, []), B = c((e) => {
|
|
116
|
+
g(e), m();
|
|
117
|
+
}, [m, g]);
|
|
75
118
|
if (!r) return null;
|
|
76
|
-
let
|
|
77
|
-
|
|
78
|
-
let
|
|
79
|
-
return
|
|
80
|
-
config:
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
119
|
+
let V;
|
|
120
|
+
L?.searchPlaceholder != null && (V = O(L.searchPlaceholder, y));
|
|
121
|
+
let H = E.trim(), U = L?.searchRequired === !0, W;
|
|
122
|
+
return W = I.status === "loading" ? /* @__PURE__ */ h(M, { label: x("common.loading") }) : L == null ? /* @__PURE__ */ h(j, { message: x("picker.unavailable", { entity: i }) }) : U && H === "" ? /* @__PURE__ */ h(S, { title: x("picker.searchRequired") }) : /* @__PURE__ */ h(k, {
|
|
123
|
+
config: L,
|
|
124
|
+
entity: i,
|
|
125
|
+
search: E,
|
|
126
|
+
scope: o,
|
|
127
|
+
fetchKey: N,
|
|
128
|
+
resetKey: `${i}:${H}:${JSON.stringify(o ?? {})}:${N}`,
|
|
129
|
+
scrollRootRef: F,
|
|
130
|
+
labels: {
|
|
131
|
+
loadMore: x("list.loadMore.action"),
|
|
132
|
+
loading: x("list.loadMore.loading"),
|
|
133
|
+
end: x("list.loadMore.end"),
|
|
134
|
+
retry: x("common.actions.retry")
|
|
135
|
+
},
|
|
136
|
+
onSelectId: B
|
|
137
|
+
}), /* @__PURE__ */ h(b, {
|
|
86
138
|
isOpen: r,
|
|
87
139
|
onClose: m,
|
|
88
140
|
title: a,
|
|
89
|
-
footer:
|
|
141
|
+
footer: R,
|
|
90
142
|
initialFocus: "first-form-control",
|
|
91
|
-
|
|
143
|
+
scrollRootRef: F,
|
|
144
|
+
children: /* @__PURE__ */ h(w, {
|
|
92
145
|
searchValue: C,
|
|
93
|
-
onSearchChange:
|
|
94
|
-
searchPlaceholder:
|
|
95
|
-
searchAriaLabel:
|
|
146
|
+
onSearchChange: z,
|
|
147
|
+
searchPlaceholder: V,
|
|
148
|
+
searchAriaLabel: V,
|
|
96
149
|
searchName: "backoffice-entity-picker-search",
|
|
97
|
-
searchEnabled:
|
|
98
|
-
children: /* @__PURE__ */
|
|
150
|
+
searchEnabled: L?.searchEnabled ?? !0,
|
|
151
|
+
children: /* @__PURE__ */ h(t, {
|
|
99
152
|
fallback: (e) => {
|
|
100
153
|
let { reset: t } = e;
|
|
101
|
-
return /* @__PURE__ */ p
|
|
154
|
+
return /* @__PURE__ */ h(p, {
|
|
102
155
|
tone: "danger",
|
|
103
156
|
title: x("picker.errors.loadFailed"),
|
|
104
|
-
actions: /* @__PURE__ */
|
|
157
|
+
actions: /* @__PURE__ */ h(v, {
|
|
105
158
|
type: "button",
|
|
106
159
|
variant: "secondary",
|
|
107
160
|
onClick: () => {
|
|
108
|
-
t(),
|
|
161
|
+
t(), P((e) => e + 1);
|
|
109
162
|
},
|
|
110
163
|
children: x("common.actions.retry")
|
|
111
164
|
}),
|
|
112
165
|
children: null
|
|
113
166
|
});
|
|
114
167
|
},
|
|
115
|
-
children: /* @__PURE__ */
|
|
116
|
-
fallback: /* @__PURE__ */
|
|
117
|
-
children:
|
|
168
|
+
children: /* @__PURE__ */ h(s, {
|
|
169
|
+
fallback: /* @__PURE__ */ h(M, { label: x("common.loading") }),
|
|
170
|
+
children: W
|
|
118
171
|
})
|
|
119
172
|
})
|
|
120
173
|
})
|
|
121
174
|
});
|
|
122
175
|
};
|
|
123
176
|
//#endregion
|
|
124
|
-
export {
|
|
177
|
+
export { N as EntityIdPickerDialog, N as default };
|
|
125
178
|
|
|
126
179
|
//# sourceMappingURL=EntityIdPickerDialog.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityIdPickerDialog.js","names":[],"sources":["../../../../../src/components/backoffice/pickers/EntityIdPickerDialog.tsx"],"sourcesContent":["import {\n Suspense,\n useCallback,\n useEffect,\n useMemo,\n useState,\n type JSX,\n} from 'react';\nimport type { TFunction } from 'i18next';\nimport { useTranslation } from 'react-i18next';\nimport * as ReactRelay from 'react-relay';\n\nimport { Button } from '@plumile/ui/atomic/atoms/button/Button.js';\nimport { Modal } from '@plumile/ui/atomic/atoms/modal/Modal.js';\nimport { Spinner } from '@plumile/ui/backoffice/atoms/spinner/Spinner.js';\nimport { BackofficeEmptyState } from '@plumile/ui/backoffice/molecules/backoffice_empty_state/BackofficeEmptyState.js';\nimport {\n BackofficePickerList,\n BackofficePickerShell,\n type BackofficePickerItem,\n} from '@plumile/ui/backoffice/molecules/backoffice_picker/BackofficePicker.js';\nimport { InlineBanner } from '@plumile/ui/components/feedback/InlineBanner.js';\nimport type {\n BackofficePickerScope,\n BackofficeRuntimeEntityPickerConfig,\n I18nLabel,\n} from '@plumile/backoffice-core/types.js';\n\nimport { BackofficeErrorBoundary } from '../errors/BackofficeErrorBoundary.js';\nimport { useBackofficeReactTranslation } from '../../../i18n/useBackofficeReactTranslation.js';\nimport { useBackofficePickerEntityLoader } from '../../../provider/useBackofficeEntityLoader.js';\nimport { useBackofficeRuntimeFragment } from '../../../relay/typedRelayHooks.js';\n\nimport * as styles from './entityIdPickerDialog.css.js';\n\nconst { useLazyLoadQuery } = ReactRelay;\n\nexport type EntityIdPickerDialogProps = {\n isOpen: boolean;\n entity: string;\n title: string;\n scope?: BackofficePickerScope<Record<string, string>>;\n onClose: () => void;\n onSelectId: (id: string) => void;\n};\n\nconst PICKER_FETCH_POLICY = 'store-and-network' as const;\n\nconst resolveLabel = (label: I18nLabel, tApp: TFunction): string => {\n return label(tApp);\n};\n\ntype PickerBodyProps = {\n config: BackofficeRuntimeEntityPickerConfig;\n search: string;\n scope?: BackofficePickerScope<Record<string, string>>;\n fetchKey: number;\n onSelectId: (id: string) => void;\n};\n\ntype RuntimePickerRow = {\n title: string;\n subtitle?: string | null | undefined;\n};\n\nconst PickerBody = ({\n config,\n search,\n scope,\n fetchKey,\n onSelectId,\n}: PickerBodyProps): JSX.Element => {\n const variables = useMemo(() => {\n const trimmed = search.trim();\n return config.buildQueryVariables({ search: trimmed, scope });\n }, [config, scope, search]);\n\n const queryData = useLazyLoadQuery(config.query, variables, {\n fetchPolicy: PICKER_FETCH_POLICY,\n fetchKey,\n });\n\n const fragmentData = useBackofficeRuntimeFragment(config.fragment, queryData);\n\n const connection = config.getConnection(fragmentData);\n\n const items = useMemo<readonly BackofficePickerItem[]>(() => {\n return connection.edges.map((edge) => {\n const row = config.toRow(edge.node) as RuntimePickerRow;\n const id = config.getRowId(row);\n return {\n id,\n title: row.title,\n subtitle: row.subtitle,\n };\n });\n }, [config, connection.edges]);\n\n return (\n <BackofficePickerList\n items={items}\n onSelectItem={(item) => {\n onSelectId(item.id);\n }}\n />\n );\n};\n\nconst buildFooter = (onClose: () => void, closeLabel: string): JSX.Element => {\n return (\n <>\n <Button type=\"button\" variant=\"secondary\" onClick={onClose}>\n {closeLabel}\n </Button>\n </>\n );\n};\n\nconst PickerUnavailable = ({ message }: { message: string }) => {\n return (\n <InlineBanner tone=\"warning\" title={message}>\n {null}\n </InlineBanner>\n );\n};\n\nconst PickerLoading = ({ label }: { label: string }): JSX.Element => {\n return (\n <div className={styles.loadingState} role=\"status\" aria-live=\"polite\">\n <Spinner size={18} ariaLabel={label} />\n <span className={styles.loadingLabel}>{label}</span>\n </div>\n );\n};\n\nexport const EntityIdPickerDialog = ({\n isOpen,\n entity,\n title,\n scope,\n onClose,\n onSelectId,\n}: EntityIdPickerDialogProps): JSX.Element | null => {\n const { t: tApp } = useTranslation();\n const { t } = useBackofficeReactTranslation();\n const [search, setSearch] = useState('');\n const [fetchKey, setFetchKey] = useState(0);\n const entityState = useBackofficePickerEntityLoader(entity, {\n enabled: isOpen,\n });\n\n let pickerConfig: BackofficeRuntimeEntityPickerConfig | undefined;\n if (entityState.status === 'loaded') {\n pickerConfig = entityState.module.config.picker as\n BackofficeRuntimeEntityPickerConfig | undefined;\n }\n\n useEffect(() => {\n if (isOpen) {\n setSearch('');\n setFetchKey((value) => {\n return value + 1;\n });\n }\n }, [isOpen]);\n\n const footer = useMemo(() => {\n return buildFooter(onClose, t('common.actions.close'));\n }, [onClose, t]);\n\n const handleSearchChange = useCallback((next: string) => {\n setSearch(next);\n setFetchKey((value) => {\n return value + 1;\n });\n }, []);\n\n const handleSelectId = useCallback(\n (id: string) => {\n onSelectId(id);\n onClose();\n },\n [onClose, onSelectId],\n );\n\n if (!isOpen) {\n return null;\n }\n\n let resolvedSearchPlaceholder: string | undefined;\n if (pickerConfig?.searchPlaceholder != null) {\n resolvedSearchPlaceholder = resolveLabel(\n pickerConfig.searchPlaceholder,\n tApp,\n );\n }\n\n const trimmedSearch = search.trim();\n const isSearchRequired = pickerConfig?.searchRequired === true;\n\n let pickerNode: JSX.Element;\n if (entityState.status === 'loading') {\n pickerNode = <PickerLoading label={t('common.loading')} />;\n } else if (pickerConfig != null) {\n if (isSearchRequired && trimmedSearch === '') {\n pickerNode = <BackofficeEmptyState title={t('picker.searchRequired')} />;\n } else {\n pickerNode = (\n <PickerBody\n config={pickerConfig}\n search={search}\n scope={scope}\n fetchKey={fetchKey}\n onSelectId={handleSelectId}\n />\n );\n }\n } else {\n pickerNode = (\n <PickerUnavailable message={t('picker.unavailable', { entity })} />\n );\n }\n\n return (\n <Modal\n isOpen={isOpen}\n onClose={onClose}\n title={title}\n footer={footer}\n initialFocus=\"first-form-control\"\n >\n <BackofficePickerShell\n searchValue={search}\n onSearchChange={handleSearchChange}\n searchPlaceholder={resolvedSearchPlaceholder}\n searchAriaLabel={resolvedSearchPlaceholder}\n searchName=\"backoffice-entity-picker-search\"\n searchEnabled={pickerConfig?.searchEnabled ?? true}\n >\n <BackofficeErrorBoundary\n fallback={(args: { error: unknown; reset: () => void }) => {\n const { reset } = args;\n return (\n <InlineBanner\n tone=\"danger\"\n title={t('picker.errors.loadFailed')}\n actions={\n <Button\n type=\"button\"\n variant=\"secondary\"\n onClick={() => {\n reset();\n setFetchKey((value) => {\n return value + 1;\n });\n }}\n >\n {t('common.actions.retry')}\n </Button>\n }\n >\n {null}\n </InlineBanner>\n );\n }}\n >\n <Suspense fallback={<PickerLoading label={t('common.loading')} />}>\n {pickerNode}\n </Suspense>\n </BackofficeErrorBoundary>\n </BackofficePickerShell>\n </Modal>\n );\n};\n\nexport type { BackofficePickerScope } from '@plumile/backoffice-core/types.js';\n\nexport default EntityIdPickerDialog;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EntityIdPickerDialog.js","names":[],"sources":["../../../../../src/components/backoffice/pickers/EntityIdPickerDialog.tsx"],"sourcesContent":["import {\n Suspense,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type JSX,\n type RefObject,\n} from 'react';\nimport type { TFunction } from 'i18next';\nimport { useTranslation } from 'react-i18next';\nimport * as ReactRelay from 'react-relay';\n\nimport { Button } from '@plumile/ui/atomic/atoms/button/Button.js';\nimport { Modal } from '@plumile/ui/atomic/atoms/modal/Modal.js';\nimport { Spinner } from '@plumile/ui/backoffice/atoms/spinner/Spinner.js';\nimport { BackofficeEmptyState } from '@plumile/ui/backoffice/molecules/backoffice_empty_state/BackofficeEmptyState.js';\nimport {\n BackofficePickerList,\n BackofficePickerShell,\n type BackofficePickerItem,\n} from '@plumile/ui/backoffice/molecules/backoffice_picker/BackofficePicker.js';\nimport { InlineBanner } from '@plumile/ui/components/feedback/InlineBanner.js';\nimport { InfiniteScrollTrigger } from '@plumile/ui/components/infinite/InfiniteScrollTrigger.js';\nimport type {\n BackofficePickerScope,\n BackofficeRuntimeEntityPickerConfig,\n I18nLabel,\n} from '@plumile/backoffice-core/types.js';\n\nimport { BackofficeErrorBoundary } from '../errors/BackofficeErrorBoundary.js';\nimport { useBackofficeReactTranslation } from '../../../i18n/useBackofficeReactTranslation.js';\nimport { useBackofficePickerEntityLoader } from '../../../provider/useBackofficeEntityLoader.js';\nimport { useInfiniteConnection } from '../../../hooks/useInfiniteConnection.js';\nimport { useBackofficeRuntimePaginationFragment } from '../../../relay/typedRelayHooks.js';\n\nimport * as styles from './entityIdPickerDialog.css.js';\n\nconst { useLazyLoadQuery } = ReactRelay;\n\nexport type EntityIdPickerDialogProps = {\n isOpen: boolean;\n entity: string;\n title: string;\n scope?: BackofficePickerScope<Record<string, string>>;\n onClose: () => void;\n onSelectId: (id: string) => void;\n};\n\nconst PICKER_FETCH_POLICY = 'store-and-network' as const;\n\nconst resolveLabel = (label: I18nLabel, tApp: TFunction): string => {\n return label(tApp);\n};\n\ntype PickerBodyProps = {\n config: BackofficeRuntimeEntityPickerConfig;\n entity: string;\n search: string;\n scope?: BackofficePickerScope<Record<string, string>>;\n fetchKey: number;\n resetKey: string;\n scrollRootRef: RefObject<Element | null>;\n labels: {\n loadMore: string;\n loading: string;\n end: string;\n retry: string;\n };\n onSelectId: (id: string) => void;\n};\n\ntype RuntimePickerRow = {\n title: string;\n subtitle?: string | null | undefined;\n};\n\nconst PickerBody = ({\n config,\n entity,\n search,\n scope,\n fetchKey,\n resetKey,\n scrollRootRef,\n labels,\n onSelectId,\n}: PickerBodyProps): JSX.Element => {\n const variables = useMemo(() => {\n const trimmed = search.trim();\n return config.buildQueryVariables({ search: trimmed, scope });\n }, [config, scope, search]);\n\n const queryData = useLazyLoadQuery(config.query, variables, {\n fetchPolicy: PICKER_FETCH_POLICY,\n fetchKey,\n });\n\n const {\n data: fragmentData,\n loadNext,\n hasNext,\n isLoadingNext,\n } = useBackofficeRuntimePaginationFragment(config.fragment, queryData);\n\n const connection = config.getConnection(fragmentData);\n\n const items = useMemo<readonly BackofficePickerItem[]>(() => {\n return connection.edges.map((edge) => {\n const row = config.toRow(edge.node) as RuntimePickerRow;\n const id = config.getRowId(row);\n return {\n id,\n title: row.title,\n subtitle: row.subtitle,\n };\n });\n }, [config, connection.edges]);\n\n const runLoadMore = useCallback(\n (onComplete: (error: Error | null) => void) => {\n loadNext(20, { onComplete });\n },\n [loadNext],\n );\n const pagination = useInfiniteConnection({\n nodes: items,\n hasNext,\n isLoadingNext,\n runLoadMore,\n surface: `backoffice.entity-picker.${entity}`,\n resetKey,\n });\n\n return (\n <>\n <BackofficePickerList\n items={pagination.nodes}\n onSelectItem={(item) => {\n onSelectId(item.id);\n }}\n />\n <InfiniteScrollTrigger\n disabled={pagination.incrementalError != null}\n hasNext={pagination.hasNext}\n isLoading={pagination.isLoadingNext}\n onLoadMore={pagination.loadMoreSafe}\n scrollRootRef={scrollRootRef}\n labels={{\n loadMore: labels.loadMore,\n loading: labels.loading,\n end: labels.end,\n }}\n />\n {pagination.incrementalError != null && (\n <InlineBanner\n tone=\"danger\"\n title={pagination.incrementalError.message}\n actions={\n <Button\n type=\"button\"\n variant=\"secondary\"\n onClick={pagination.retryLoadMore}\n >\n {labels.retry}\n </Button>\n }\n >\n {null}\n </InlineBanner>\n )}\n </>\n );\n};\n\nconst buildFooter = (onClose: () => void, closeLabel: string): JSX.Element => {\n return (\n <>\n <Button type=\"button\" variant=\"secondary\" onClick={onClose}>\n {closeLabel}\n </Button>\n </>\n );\n};\n\nconst PickerUnavailable = ({ message }: { message: string }) => {\n return (\n <InlineBanner tone=\"warning\" title={message}>\n {null}\n </InlineBanner>\n );\n};\n\nconst PickerLoading = ({ label }: { label: string }): JSX.Element => {\n return (\n <div className={styles.loadingState} role=\"status\" aria-live=\"polite\">\n <Spinner size={18} ariaLabel={label} />\n <span className={styles.loadingLabel}>{label}</span>\n </div>\n );\n};\n\nexport const EntityIdPickerDialog = ({\n isOpen,\n entity,\n title,\n scope,\n onClose,\n onSelectId,\n}: EntityIdPickerDialogProps): JSX.Element | null => {\n const { t: tApp } = useTranslation();\n const { t } = useBackofficeReactTranslation();\n const [search, setSearch] = useState('');\n const [executedSearch, setExecutedSearch] = useState('');\n const [fetchKey, setFetchKey] = useState(0);\n const scrollRootRef = useRef<HTMLDivElement | null>(null);\n const entityState = useBackofficePickerEntityLoader(entity, {\n enabled: isOpen,\n });\n\n let pickerConfig: BackofficeRuntimeEntityPickerConfig | undefined;\n if (entityState.status === 'loaded') {\n pickerConfig = entityState.module.config.picker as\n BackofficeRuntimeEntityPickerConfig | undefined;\n }\n\n useEffect(() => {\n if (isOpen) {\n setSearch('');\n setExecutedSearch('');\n setFetchKey((value) => {\n return value + 1;\n });\n }\n }, [isOpen]);\n\n useEffect(() => {\n const timeout = setTimeout(() => {\n setExecutedSearch(search);\n }, 250);\n return () => {\n clearTimeout(timeout);\n };\n }, [search]);\n\n const footer = useMemo(() => {\n return buildFooter(onClose, t('common.actions.close'));\n }, [onClose, t]);\n\n const handleSearchChange = useCallback((next: string) => {\n setSearch(next);\n }, []);\n\n const handleSelectId = useCallback(\n (id: string) => {\n onSelectId(id);\n onClose();\n },\n [onClose, onSelectId],\n );\n\n if (!isOpen) {\n return null;\n }\n\n let resolvedSearchPlaceholder: string | undefined;\n if (pickerConfig?.searchPlaceholder != null) {\n resolvedSearchPlaceholder = resolveLabel(\n pickerConfig.searchPlaceholder,\n tApp,\n );\n }\n\n const trimmedSearch = executedSearch.trim();\n const isSearchRequired = pickerConfig?.searchRequired === true;\n\n let pickerNode: JSX.Element;\n if (entityState.status === 'loading') {\n pickerNode = <PickerLoading label={t('common.loading')} />;\n } else if (pickerConfig != null) {\n if (isSearchRequired && trimmedSearch === '') {\n pickerNode = <BackofficeEmptyState title={t('picker.searchRequired')} />;\n } else {\n pickerNode = (\n <PickerBody\n config={pickerConfig}\n entity={entity}\n search={executedSearch}\n scope={scope}\n fetchKey={fetchKey}\n resetKey={`${entity}:${trimmedSearch}:${JSON.stringify(scope ?? {})}:${fetchKey}`}\n scrollRootRef={scrollRootRef}\n labels={{\n loadMore: t('list.loadMore.action'),\n loading: t('list.loadMore.loading'),\n end: t('list.loadMore.end'),\n retry: t('common.actions.retry'),\n }}\n onSelectId={handleSelectId}\n />\n );\n }\n } else {\n pickerNode = (\n <PickerUnavailable message={t('picker.unavailable', { entity })} />\n );\n }\n\n return (\n <Modal\n isOpen={isOpen}\n onClose={onClose}\n title={title}\n footer={footer}\n initialFocus=\"first-form-control\"\n scrollRootRef={scrollRootRef}\n >\n <BackofficePickerShell\n searchValue={search}\n onSearchChange={handleSearchChange}\n searchPlaceholder={resolvedSearchPlaceholder}\n searchAriaLabel={resolvedSearchPlaceholder}\n searchName=\"backoffice-entity-picker-search\"\n searchEnabled={pickerConfig?.searchEnabled ?? true}\n >\n <BackofficeErrorBoundary\n fallback={(args: { error: unknown; reset: () => void }) => {\n const { reset } = args;\n return (\n <InlineBanner\n tone=\"danger\"\n title={t('picker.errors.loadFailed')}\n actions={\n <Button\n type=\"button\"\n variant=\"secondary\"\n onClick={() => {\n reset();\n setFetchKey((value) => {\n return value + 1;\n });\n }}\n >\n {t('common.actions.retry')}\n </Button>\n }\n >\n {null}\n </InlineBanner>\n );\n }}\n >\n <Suspense fallback={<PickerLoading label={t('common.loading')} />}>\n {pickerNode}\n </Suspense>\n </BackofficeErrorBoundary>\n </BackofficePickerShell>\n </Modal>\n );\n};\n\nexport type { BackofficePickerScope } from '@plumile/backoffice-core/types.js';\n\nexport default EntityIdPickerDialog;\n"],"mappings":"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"}
|