@dataverse-kit/form-runtime 0.1.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.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +93 -0
  3. package/dist/businessRules-U1_MBgyG.d.cts +372 -0
  4. package/dist/businessRules-U1_MBgyG.d.ts +372 -0
  5. package/dist/context.cjs +151 -0
  6. package/dist/context.cjs.map +1 -0
  7. package/dist/context.d.cts +132 -0
  8. package/dist/context.d.ts +132 -0
  9. package/dist/context.mjs +113 -0
  10. package/dist/context.mjs.map +1 -0
  11. package/dist/control-DFOg_pc_.d.cts +1027 -0
  12. package/dist/control-DaXBm-52.d.ts +1027 -0
  13. package/dist/gridCustomizer-C0V9FAE_.d.ts +569 -0
  14. package/dist/gridCustomizer-mJO-kmQ4.d.cts +569 -0
  15. package/dist/hooks.cjs +85 -0
  16. package/dist/hooks.cjs.map +1 -0
  17. package/dist/hooks.d.cts +24 -0
  18. package/dist/hooks.d.ts +24 -0
  19. package/dist/hooks.mjs +60 -0
  20. package/dist/hooks.mjs.map +1 -0
  21. package/dist/icons.cjs +202 -0
  22. package/dist/icons.cjs.map +1 -0
  23. package/dist/icons.d.cts +130 -0
  24. package/dist/icons.d.ts +130 -0
  25. package/dist/icons.mjs +165 -0
  26. package/dist/icons.mjs.map +1 -0
  27. package/dist/index.cjs +6509 -0
  28. package/dist/index.cjs.map +1 -0
  29. package/dist/index.d.cts +410 -0
  30. package/dist/index.d.ts +410 -0
  31. package/dist/index.mjs +6490 -0
  32. package/dist/index.mjs.map +1 -0
  33. package/dist/runtime-capabilities-BdGDdu0d.d.cts +119 -0
  34. package/dist/runtime-capabilities-Brfc7loJ.d.ts +119 -0
  35. package/dist/theme-BfeZIxmZ.d.cts +74 -0
  36. package/dist/theme-BfeZIxmZ.d.ts +74 -0
  37. package/dist/theme.cjs +215 -0
  38. package/dist/theme.cjs.map +1 -0
  39. package/dist/theme.d.cts +32 -0
  40. package/dist/theme.d.ts +32 -0
  41. package/dist/theme.mjs +186 -0
  42. package/dist/theme.mjs.map +1 -0
  43. package/dist/types.cjs +976 -0
  44. package/dist/types.cjs.map +1 -0
  45. package/dist/types.d.cts +813 -0
  46. package/dist/types.d.ts +813 -0
  47. package/dist/types.mjs +902 -0
  48. package/dist/types.mjs.map +1 -0
  49. package/dist/utils.cjs +250 -0
  50. package/dist/utils.cjs.map +1 -0
  51. package/dist/utils.d.cts +99 -0
  52. package/dist/utils.d.ts +99 -0
  53. package/dist/utils.mjs +220 -0
  54. package/dist/utils.mjs.map +1 -0
  55. package/dist/v8.cjs +4622 -0
  56. package/dist/v8.cjs.map +1 -0
  57. package/dist/v8.d.cts +730 -0
  58. package/dist/v8.d.ts +730 -0
  59. package/dist/v8.mjs +4622 -0
  60. package/dist/v8.mjs.map +1 -0
  61. package/dist/v9.cjs +19 -0
  62. package/dist/v9.cjs.map +1 -0
  63. package/dist/v9.d.cts +2 -0
  64. package/dist/v9.d.ts +2 -0
  65. package/dist/v9.mjs +1 -0
  66. package/dist/v9.mjs.map +1 -0
  67. package/package.json +113 -0
package/dist/v8.cjs ADDED
@@ -0,0 +1,4622 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/v8/index.ts
31
+ var v8_exports = {};
32
+ __export(v8_exports, {
33
+ ButtonControl: () => ButtonControl,
34
+ ChartBranch: () => ChartBranch,
35
+ ChartRenderer: () => ChartRenderer,
36
+ CheckboxControl: () => CheckboxControl,
37
+ ChoiceGroupBranch: () => ChoiceGroupBranch,
38
+ ChoiceGroupControl: () => ChoiceGroupControl,
39
+ ComboBoxControl: () => ComboBoxControl,
40
+ DateControl: () => DateControl,
41
+ DropdownControl: () => DropdownControl,
42
+ EmojiRating: () => EmojiRating,
43
+ FocusedViewMasterDetailPreview: () => FocusedViewMasterDetailPreview,
44
+ FormLinkControl: () => FormLinkControl,
45
+ LabelControl: () => LabelControl,
46
+ LookupBranch: () => LookupBranch,
47
+ LookupControl: () => LookupControl,
48
+ NestedRecordsCallout: () => NestedRecordsCallout,
49
+ NestedRecordsSidePanel: () => NestedRecordsSidePanel,
50
+ NestedRecordsTrigger: () => NestedRecordsTrigger,
51
+ NumberControl: () => NumberControl,
52
+ PersonaControl: () => PersonaControl,
53
+ RatingControl: () => RatingControl,
54
+ SliderControl: () => SliderControl,
55
+ SpacerControl: () => SpacerControl,
56
+ SpinButtonControl: () => SpinButtonControl,
57
+ SubgridBranch: () => SubgridBranch,
58
+ TextControl: () => TextControl,
59
+ TextFieldWithFormat: () => TextFieldWithFormat,
60
+ TextareaControl: () => TextareaControl,
61
+ TimelineControl: () => TimelineControl,
62
+ ToggleControl: () => ToggleControl,
63
+ UnknownControl: () => UnknownControl,
64
+ WebResourceBranch: () => WebResourceBranch,
65
+ WebResourceRenderer: () => WebResourceRenderer,
66
+ isChartControlType: () => isChartControlType,
67
+ isDisabled: () => isDisabled,
68
+ isEmbeddedEntityBrowser: () => isEmbeddedEntityBrowser,
69
+ lookupEntityHelpers: () => lookupEntityHelpers,
70
+ renderGridCell: () => renderGridCell
71
+ });
72
+ module.exports = __toCommonJS(v8_exports);
73
+
74
+ // src/v8/controls/types.ts
75
+ function isDisabled(control, interactive) {
76
+ return !interactive || control.readOnly === true;
77
+ }
78
+
79
+ // src/v8/controls/spacer.tsx
80
+ var import_jsx_runtime = require("react/jsx-runtime");
81
+ var SpacerControl = ({ control, interactive }) => {
82
+ const designOutline = !interactive;
83
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
84
+ "div",
85
+ {
86
+ style: {
87
+ height: control.properties.height ?? 16,
88
+ backgroundColor: designOutline ? "#f3f2f1" : "transparent",
89
+ border: designOutline ? "1px dashed #c8c6c4" : "none",
90
+ borderRadius: 2
91
+ }
92
+ }
93
+ );
94
+ };
95
+
96
+ // src/v8/controls/label.tsx
97
+ var import_react = __toESM(require("react"), 1);
98
+ var import_dompurify = __toESM(require("dompurify"), 1);
99
+ var import_jsx_runtime2 = require("react/jsx-runtime");
100
+ var IS_BROWSER = typeof window !== "undefined" && typeof document !== "undefined";
101
+ var PURIFY_CONFIG = {
102
+ // Block phishing-shaped elements. DOMPurify 3.x defaults permit
103
+ // <form>, <input>, <button>, <select>, <textarea>, <option> — an
104
+ // attacker-controlled label could render a fake login form
105
+ // indistinguishable from the host app's chrome.
106
+ FORBID_TAGS: ["form", "input", "button", "select", "textarea", "option"],
107
+ // Drop `target` outright. Reverse-tabnabbing (`target="_blank"`
108
+ // without `rel="noopener noreferrer"`) is the cousin bypass to
109
+ // `javascript:` URIs and DOMPurify doesn't auto-rel-noopener.
110
+ // Labels have no legitimate need to escape the current frame.
111
+ FORBID_ATTR: ["target"]
112
+ };
113
+ function sanitizeLabelHtml(text) {
114
+ if (!IS_BROWSER) {
115
+ return text.replace(/<[^>]*>/g, "");
116
+ }
117
+ return String(import_dompurify.default.sanitize(text, PURIFY_CONFIG));
118
+ }
119
+ var LabelControl = ({ control, interactive }) => {
120
+ const text = String(control.properties.text ?? "");
121
+ const variant = control.properties.variant ?? "medium";
122
+ const fontWeight = control.properties.fontWeight ?? "normal";
123
+ const color = control.properties.color ?? "#323130";
124
+ const alignment = control.properties.alignment ?? "left";
125
+ const fontSizeMap = { small: 12, medium: 14, large: 16, xLarge: 20 };
126
+ const fontSize = fontSizeMap[variant] ?? 14;
127
+ const hasHtml = !!text && /<[^>]+>/.test(text);
128
+ const safeHtml = import_react.default.useMemo(() => hasHtml ? sanitizeLabelHtml(text) : "", [hasHtml, text]);
129
+ const renderableHtml = hasHtml ? safeHtml.trim() : "";
130
+ const strippedText = text ? text.replace(/<[^>]*>/g, "").trim() : "";
131
+ const isEmpty = !strippedText && !renderableHtml;
132
+ const designMode = !interactive;
133
+ const useHtmlBranch = hasHtml && !!renderableHtml;
134
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
135
+ "div",
136
+ {
137
+ style: {
138
+ fontSize,
139
+ fontWeight,
140
+ color,
141
+ textAlign: alignment,
142
+ padding: "4px 0",
143
+ border: designMode && isEmpty ? "1px dashed #c8c6c4" : "none",
144
+ borderRadius: 2,
145
+ minHeight: designMode ? 24 : "auto"
146
+ },
147
+ ...useHtmlBranch ? { dangerouslySetInnerHTML: { __html: safeHtml } } : {
148
+ // When `hasHtml` was true but sanitize emptied the string,
149
+ // show the design-mode placeholder rather than the raw
150
+ // pre-sanitize text — leaking literal `<script>...</script>`
151
+ // chars into the label would be confusing.
152
+ children: hasHtml ? designMode ? "(Enter label text)" : "" : text || (designMode ? "(Enter label text)" : "")
153
+ }
154
+ }
155
+ );
156
+ };
157
+
158
+ // src/v8/controls/formlink.tsx
159
+ var import_react2 = require("@fluentui/react");
160
+ var import_jsx_runtime3 = require("react/jsx-runtime");
161
+ var FormLinkControl = ({
162
+ control,
163
+ interactive,
164
+ onButtonClick
165
+ }) => {
166
+ const linkText = control.properties.linkText || "Open form";
167
+ const iconName = control.properties.iconName || "OpenInNewWindow";
168
+ const showIcon = control.properties.showIcon ?? true;
169
+ const linkMode = control.properties.linkMode || "projectForm";
170
+ const openInNewTab = control.properties.openInNewTab ?? true;
171
+ const recordUrl = control.properties.recordUrl || "";
172
+ const disabled = isDisabled(control, interactive);
173
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
174
+ import_react2.Link,
175
+ {
176
+ href: linkMode === "recordUrl" && recordUrl ? recordUrl : void 0,
177
+ target: openInNewTab ? "_blank" : void 0,
178
+ disabled,
179
+ onClick: onButtonClick ? () => onButtonClick({ control }) : void 0,
180
+ styles: { root: { display: "inline-flex", alignItems: "center", gap: 4 } },
181
+ children: [
182
+ showIcon && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react2.Icon, { iconName, styles: { root: { fontSize: 14 } } }),
183
+ linkText
184
+ ]
185
+ }
186
+ );
187
+ };
188
+
189
+ // src/v8/controls/unknown.tsx
190
+ var import_react5 = require("@fluentui/react");
191
+
192
+ // src/v8/controls/internals/TextFieldWithFormat.tsx
193
+ var import_react3 = require("react");
194
+ var import_react4 = require("@fluentui/react");
195
+ var import_jsx_runtime4 = require("react/jsx-runtime");
196
+ var containerClass = (0, import_react4.mergeStyles)({
197
+ display: "flex",
198
+ alignItems: "center",
199
+ gap: 4,
200
+ width: "100%"
201
+ });
202
+ var textFieldWrapperClass = (0, import_react4.mergeStyles)({
203
+ flex: 1,
204
+ position: "relative"
205
+ });
206
+ var prefixIconContainerClass = (0, import_react4.mergeStyles)({
207
+ position: "absolute",
208
+ left: 8,
209
+ top: "50%",
210
+ transform: "translateY(-50%)",
211
+ display: "flex",
212
+ alignItems: "center",
213
+ justifyContent: "center",
214
+ pointerEvents: "none",
215
+ zIndex: 1
216
+ });
217
+ var actionButtonClass = (0, import_react4.mergeStyles)({
218
+ flexShrink: 0
219
+ });
220
+ function getInputType(format, showPassword) {
221
+ switch (format) {
222
+ case "email":
223
+ return "email";
224
+ case "url":
225
+ return "url";
226
+ case "phone":
227
+ return "tel";
228
+ case "password":
229
+ return showPassword ? "text" : "password";
230
+ default:
231
+ return "text";
232
+ }
233
+ }
234
+ function getIconColor(format) {
235
+ switch (format) {
236
+ case "email":
237
+ case "url":
238
+ return "#0078d4";
239
+ case "phone":
240
+ return "#107c10";
241
+ case "password":
242
+ default:
243
+ return "#605e5c";
244
+ }
245
+ }
246
+ function getActionConfig(format, value) {
247
+ if (!value) return null;
248
+ switch (format) {
249
+ case "email":
250
+ return {
251
+ iconName: "Mail",
252
+ title: "Send email",
253
+ href: `mailto:${value}`,
254
+ ariaLabel: "Send email"
255
+ };
256
+ case "url": {
257
+ const urlWithProtocol = value.startsWith("http://") || value.startsWith("https://") ? value : `https://${value}`;
258
+ return {
259
+ iconName: "OpenInNewWindow",
260
+ title: "Open link",
261
+ href: urlWithProtocol,
262
+ ariaLabel: "Open link in new tab"
263
+ };
264
+ }
265
+ case "phone":
266
+ return {
267
+ iconName: "Phone",
268
+ title: "Call",
269
+ href: `tel:${value.replace(/[^\d+]/g, "")}`,
270
+ ariaLabel: "Make phone call"
271
+ };
272
+ default:
273
+ return null;
274
+ }
275
+ }
276
+ var TextFieldWithFormat = ({
277
+ textFormat,
278
+ placeholder,
279
+ disabled = false,
280
+ value,
281
+ maxLength,
282
+ onChange,
283
+ showActionButton = false,
284
+ allowPasswordToggle = true,
285
+ iconName
286
+ }) => {
287
+ const [showPassword, setShowPassword] = (0, import_react3.useState)(false);
288
+ const showIconPrefix = !!iconName && textFormat !== "text";
289
+ const inputType = (0, import_react3.useMemo)(
290
+ () => getInputType(textFormat, showPassword),
291
+ [textFormat, showPassword]
292
+ );
293
+ const actionConfig = (0, import_react3.useMemo)(
294
+ () => showActionButton ? getActionConfig(textFormat, value) : null,
295
+ [showActionButton, textFormat, value]
296
+ );
297
+ const handleActionClick = () => {
298
+ if (actionConfig?.href) {
299
+ if (textFormat === "url") {
300
+ window.open(actionConfig.href, "_blank", "noopener,noreferrer");
301
+ } else {
302
+ window.location.href = actionConfig.href;
303
+ }
304
+ }
305
+ };
306
+ const handlePasswordToggle = () => setShowPassword(!showPassword);
307
+ const textFieldStyles = (0, import_react3.useMemo)(
308
+ () => ({
309
+ root: { width: "100%" },
310
+ fieldGroup: showIconPrefix ? { paddingLeft: 28 } : void 0
311
+ }),
312
+ [showIconPrefix]
313
+ );
314
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: containerClass, children: [
315
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: textFieldWrapperClass, children: [
316
+ showIconPrefix && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: prefixIconContainerClass, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
317
+ import_react4.Icon,
318
+ {
319
+ iconName,
320
+ styles: {
321
+ root: {
322
+ fontSize: 14,
323
+ color: disabled ? "#a19f9d" : getIconColor(textFormat)
324
+ }
325
+ }
326
+ }
327
+ ) }),
328
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
329
+ import_react4.TextField,
330
+ {
331
+ type: inputType,
332
+ placeholder,
333
+ disabled,
334
+ value,
335
+ maxLength,
336
+ onChange,
337
+ styles: textFieldStyles
338
+ }
339
+ )
340
+ ] }),
341
+ textFormat === "password" && allowPasswordToggle && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
342
+ import_react4.IconButton,
343
+ {
344
+ iconProps: { iconName: showPassword ? "Hide" : "View" },
345
+ title: showPassword ? "Hide password" : "Show password",
346
+ ariaLabel: showPassword ? "Hide password" : "Show password",
347
+ onClick: handlePasswordToggle,
348
+ disabled,
349
+ className: actionButtonClass,
350
+ styles: {
351
+ root: { height: 32, width: 32 },
352
+ icon: { fontSize: 14, color: "#605e5c" }
353
+ }
354
+ }
355
+ ),
356
+ actionConfig && !disabled && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
357
+ import_react4.IconButton,
358
+ {
359
+ iconProps: { iconName: actionConfig.iconName },
360
+ title: actionConfig.title,
361
+ ariaLabel: actionConfig.ariaLabel,
362
+ onClick: handleActionClick,
363
+ className: actionButtonClass,
364
+ styles: {
365
+ root: { height: 32, width: 32 },
366
+ icon: { fontSize: 14, color: getIconColor(textFormat) }
367
+ }
368
+ }
369
+ )
370
+ ] });
371
+ };
372
+
373
+ // src/v8/controls/unknown.tsx
374
+ var import_jsx_runtime5 = require("react/jsx-runtime");
375
+ var LEGACY_FORMAT_MAP = {
376
+ phone: "phone",
377
+ email: "email",
378
+ url: "url"
379
+ };
380
+ var UnknownControl = ({
381
+ control,
382
+ displayValue,
383
+ hasValue,
384
+ hasControlledValue,
385
+ interactive,
386
+ onChange
387
+ }) => {
388
+ const disabled = isDisabled(control, interactive);
389
+ const inferredFormat = LEGACY_FORMAT_MAP[control.type] ?? "text";
390
+ const isLegacyTextType = inferredFormat !== "text";
391
+ if (isLegacyTextType) {
392
+ const showPlaceholder = control.properties.showPlaceholder ?? false;
393
+ const customPlaceholder = control.properties.placeholder;
394
+ const placeholderMap = {
395
+ text: control.label,
396
+ email: "email@example.com",
397
+ url: "https://example.com",
398
+ phone: "+1 (555) 000-0000",
399
+ password: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"
400
+ };
401
+ const placeholder = showPlaceholder ? customPlaceholder ?? placeholderMap[inferredFormat] : void 0;
402
+ const iconMap = {
403
+ text: void 0,
404
+ email: "Mail",
405
+ url: "Link",
406
+ phone: "Phone",
407
+ password: "Lock"
408
+ };
409
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
410
+ TextFieldWithFormat,
411
+ {
412
+ textFormat: inferredFormat,
413
+ placeholder,
414
+ disabled,
415
+ value: hasControlledValue ? displayValue : hasValue ? displayValue : void 0,
416
+ maxLength: control.properties.maxLength,
417
+ onChange: onChange ? (_, newValue) => onChange(newValue ?? "") : void 0,
418
+ showActionButton: control.properties.showActionButton ?? true,
419
+ allowPasswordToggle: true,
420
+ iconName: iconMap[inferredFormat]
421
+ }
422
+ );
423
+ }
424
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
425
+ import_react5.TextField,
426
+ {
427
+ placeholder: control.label,
428
+ disabled,
429
+ value: hasValue ? displayValue : void 0,
430
+ onChange: onChange ? (_, newValue) => onChange(newValue ?? "") : void 0
431
+ }
432
+ );
433
+ };
434
+
435
+ // src/v8/controls/text.tsx
436
+ var import_jsx_runtime6 = require("react/jsx-runtime");
437
+ var TextControl = ({
438
+ control,
439
+ displayValue,
440
+ hasValue,
441
+ hasControlledValue,
442
+ interactive,
443
+ onChange
444
+ }) => {
445
+ const disabled = isDisabled(control, interactive);
446
+ const textFormat = control.properties.textFormat ?? "text";
447
+ const showPlaceholder = control.properties.showPlaceholder ?? false;
448
+ const customPlaceholder = control.properties.placeholder;
449
+ const showActionButton = control.properties.showActionButton ?? false;
450
+ const allowPasswordToggle = control.properties.allowPasswordToggle ?? true;
451
+ const placeholderMap = {
452
+ text: control.label,
453
+ email: "email@example.com",
454
+ url: "https://example.com",
455
+ phone: "+1 (555) 000-0000",
456
+ password: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"
457
+ };
458
+ const placeholder = showPlaceholder ? customPlaceholder ?? placeholderMap[textFormat] : void 0;
459
+ const iconMap = {
460
+ text: void 0,
461
+ email: "Mail",
462
+ url: "Link",
463
+ phone: "Phone",
464
+ password: "Lock"
465
+ };
466
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
467
+ TextFieldWithFormat,
468
+ {
469
+ textFormat,
470
+ placeholder,
471
+ disabled,
472
+ value: hasControlledValue ? displayValue : hasValue ? displayValue : void 0,
473
+ maxLength: control.properties.maxLength,
474
+ onChange: onChange ? (_, newValue) => onChange(newValue ?? "") : void 0,
475
+ showActionButton,
476
+ allowPasswordToggle,
477
+ iconName: iconMap[textFormat]
478
+ }
479
+ );
480
+ };
481
+
482
+ // src/v8/controls/textarea.tsx
483
+ var import_react6 = require("@fluentui/react");
484
+ var import_jsx_runtime7 = require("react/jsx-runtime");
485
+ var TextareaControl = ({
486
+ control,
487
+ displayValue,
488
+ hasValue,
489
+ hasControlledValue,
490
+ interactive,
491
+ onChange
492
+ }) => {
493
+ const disabled = isDisabled(control, interactive);
494
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
495
+ import_react6.TextField,
496
+ {
497
+ multiline: true,
498
+ rows: control.properties.rows ?? 3,
499
+ disabled,
500
+ value: hasControlledValue ? displayValue : hasValue ? displayValue : void 0,
501
+ defaultValue: hasControlledValue || hasValue ? void 0 : "",
502
+ onChange: onChange ? (_, newValue) => onChange(newValue ?? "") : void 0,
503
+ styles: control.properties.resizable === false ? { field: { resize: "none" } } : void 0
504
+ }
505
+ );
506
+ };
507
+
508
+ // src/v8/controls/number.tsx
509
+ var import_react7 = require("@fluentui/react");
510
+ var import_jsx_runtime8 = require("react/jsx-runtime");
511
+ var NumberControl = ({
512
+ control,
513
+ displayValue,
514
+ hasValue,
515
+ interactive,
516
+ onChange
517
+ }) => {
518
+ const disabled = isDisabled(control, interactive);
519
+ const step = control.type === "number" ? 1 : 0.01;
520
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
521
+ import_react7.SpinButton,
522
+ {
523
+ disabled,
524
+ step,
525
+ value: hasValue ? displayValue : "",
526
+ incrementButtonAriaLabel: "Increase",
527
+ decrementButtonAriaLabel: "Decrease",
528
+ onChange: onChange ? (_, newValue) => onChange(newValue ?? "") : void 0
529
+ }
530
+ );
531
+ };
532
+
533
+ // src/v8/controls/date.tsx
534
+ var import_react8 = require("@fluentui/react");
535
+ var import_jsx_runtime9 = require("react/jsx-runtime");
536
+ var DateControl = ({
537
+ control,
538
+ effectiveValue,
539
+ hasValue,
540
+ interactive,
541
+ onChange
542
+ }) => {
543
+ const disabled = isDisabled(control, interactive);
544
+ let dateValue;
545
+ if (hasValue && effectiveValue) {
546
+ const parsed = effectiveValue instanceof Date ? effectiveValue : new Date(String(effectiveValue));
547
+ if (!isNaN(parsed.getTime())) {
548
+ dateValue = parsed;
549
+ }
550
+ }
551
+ const showPlaceholder = control.properties.showPlaceholder ?? false;
552
+ const customPlaceholder = control.properties.placeholder;
553
+ const placeholder = showPlaceholder ? customPlaceholder ?? (control.type === "datetime" ? "Select date and time" : "Select date") : void 0;
554
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
555
+ import_react8.DatePicker,
556
+ {
557
+ placeholder,
558
+ disabled,
559
+ ariaLabel: control.label,
560
+ value: dateValue,
561
+ onSelectDate: onChange ? (date) => onChange(date ?? null) : void 0
562
+ }
563
+ );
564
+ };
565
+
566
+ // src/v8/controls/checkbox.tsx
567
+ var import_react9 = require("@fluentui/react");
568
+ var import_jsx_runtime10 = require("react/jsx-runtime");
569
+ var CheckboxControl = ({
570
+ control,
571
+ effectiveValue,
572
+ hasValue,
573
+ interactive,
574
+ onChange
575
+ }) => {
576
+ const disabled = isDisabled(control, interactive);
577
+ const checked = hasValue ? Boolean(effectiveValue) : void 0;
578
+ const boxSide = control.properties.boxSide ?? "end";
579
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
580
+ import_react9.Checkbox,
581
+ {
582
+ label: "",
583
+ boxSide,
584
+ disabled,
585
+ checked,
586
+ onChange: onChange ? (_, isChecked) => onChange(!!isChecked) : void 0
587
+ }
588
+ );
589
+ };
590
+
591
+ // src/v8/controls/toggle.tsx
592
+ var import_react10 = require("@fluentui/react");
593
+ var import_jsx_runtime11 = require("react/jsx-runtime");
594
+ var ToggleControl = ({
595
+ control,
596
+ effectiveValue,
597
+ hasValue,
598
+ interactive,
599
+ onChange
600
+ }) => {
601
+ const disabled = isDisabled(control, interactive);
602
+ const checked = hasValue ? Boolean(effectiveValue) : void 0;
603
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
604
+ import_react10.Toggle,
605
+ {
606
+ onText: control.properties.onText ?? "Yes",
607
+ offText: control.properties.offText ?? "No",
608
+ disabled,
609
+ checked,
610
+ onChange: onChange ? (_, isChecked) => onChange(!!isChecked) : void 0
611
+ }
612
+ );
613
+ };
614
+
615
+ // src/v8/controls/slider.tsx
616
+ var import_react11 = require("@fluentui/react");
617
+ var import_jsx_runtime12 = require("react/jsx-runtime");
618
+ var SliderControl = ({
619
+ control,
620
+ effectiveValue,
621
+ hasValue,
622
+ interactive,
623
+ onChange
624
+ }) => {
625
+ const disabled = isDisabled(control, interactive);
626
+ const min = control.properties.min ?? 0;
627
+ const max = control.properties.max ?? 100;
628
+ const step = control.properties.step ?? 1;
629
+ const showValue = control.properties.showValue ?? true;
630
+ const vertical = control.properties.vertical ?? false;
631
+ const originFromZero = control.properties.originFromZero ?? false;
632
+ const snapToStep = control.properties.snapToStep ?? true;
633
+ const value = hasValue ? typeof effectiveValue === "number" ? effectiveValue : parseFloat(String(effectiveValue)) : void 0;
634
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
635
+ import_react11.Slider,
636
+ {
637
+ min,
638
+ max,
639
+ step,
640
+ showValue,
641
+ vertical,
642
+ originFromZero,
643
+ snapToStep,
644
+ disabled,
645
+ value: typeof value === "number" && !isNaN(value) ? value : void 0,
646
+ onChange: onChange ? (next) => onChange(next) : void 0,
647
+ styles: { root: { width: "100%" } }
648
+ }
649
+ );
650
+ };
651
+
652
+ // src/v8/controls/rating.tsx
653
+ var import_react14 = require("@fluentui/react");
654
+
655
+ // src/v8/controls/internals/EmojiRating.tsx
656
+ var import_react12 = require("react");
657
+ var import_react13 = require("@fluentui/react");
658
+ var import_jsx_runtime13 = require("react/jsx-runtime");
659
+ var EMOJI_SETS = {
660
+ 2: {
661
+ icons: ["EmojiDisappointed", "Emoji"],
662
+ colors: ["#d13438", "#107c10"],
663
+ selectedColors: ["#a4262c", "#0b6a0b"],
664
+ labels: ["Unhappy", "Happy"]
665
+ },
666
+ 3: {
667
+ icons: ["EmojiDisappointed", "EmojiNeutral", "Emoji"],
668
+ colors: ["#d13438", "#c8c820", "#107c10"],
669
+ selectedColors: ["#a4262c", "#8f7b0e", "#0b6a0b"],
670
+ labels: ["Unhappy", "Neutral", "Happy"]
671
+ },
672
+ 4: {
673
+ icons: ["EmojiDisappointed", "Sad", "Emoji2", "Emoji"],
674
+ colors: ["#d13438", "#ca5010", "#498205", "#107c10"],
675
+ selectedColors: ["#a4262c", "#8e3900", "#2d5702", "#0b6a0b"],
676
+ labels: ["Very Unhappy", "Unhappy", "Happy", "Very Happy"]
677
+ },
678
+ 5: {
679
+ icons: ["EmojiDisappointed", "Sad", "EmojiNeutral", "Emoji2", "Emoji"],
680
+ colors: ["#d13438", "#ca5010", "#c8c820", "#498205", "#107c10"],
681
+ selectedColors: ["#a4262c", "#8e3900", "#8f7b0e", "#2d5702", "#0b6a0b"],
682
+ labels: ["Very Unhappy", "Unhappy", "Neutral", "Happy", "Very Happy"]
683
+ }
684
+ };
685
+ function getEmojiSet(count) {
686
+ const clamped = Math.max(2, Math.min(count, 5));
687
+ return EMOJI_SETS[clamped];
688
+ }
689
+ var EmojiRating = ({
690
+ max,
691
+ size,
692
+ useColor,
693
+ customEmojiColor,
694
+ disabled
695
+ }) => {
696
+ const [selectedIndex, setSelectedIndex] = (0, import_react12.useState)(null);
697
+ const [hoveredIndex, setHoveredIndex] = (0, import_react12.useState)(null);
698
+ const iconSizeMap = { small: 20, medium: 28, large: 36 };
699
+ const baseSize = iconSizeMap[size] ?? 28;
700
+ const emojiSet = getEmojiSet(max);
701
+ const gap = size === "small" ? 4 : 8;
702
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
703
+ "div",
704
+ {
705
+ style: { display: "flex", gap, alignItems: "center" },
706
+ onMouseLeave: () => !disabled && setHoveredIndex(null),
707
+ children: emojiSet.icons.map((iconName, idx) => {
708
+ const isSelected = selectedIndex === idx;
709
+ const isHovered = hoveredIndex === idx;
710
+ const isActive = isSelected || isHovered;
711
+ const outlineColor = useColor ? customEmojiColor || emojiSet.colors[idx] : "#605e5c";
712
+ const fillColor = useColor ? customEmojiColor || emojiSet.selectedColors[idx] : "#323130";
713
+ const activeSize = Math.round(baseSize * 1.15);
714
+ const circleSize = isActive ? activeSize + 12 : void 0;
715
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
716
+ "div",
717
+ {
718
+ onClick: () => !disabled && setSelectedIndex(isSelected ? null : idx),
719
+ onMouseEnter: () => !disabled && setHoveredIndex(idx),
720
+ title: emojiSet.labels[idx],
721
+ style: {
722
+ display: "flex",
723
+ alignItems: "center",
724
+ justifyContent: "center",
725
+ width: circleSize,
726
+ height: circleSize,
727
+ borderRadius: "50%",
728
+ backgroundColor: isActive ? fillColor : "transparent",
729
+ cursor: disabled ? "default" : "pointer",
730
+ transition: "all 0.15s ease",
731
+ flexShrink: 0
732
+ },
733
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
734
+ import_react13.Icon,
735
+ {
736
+ iconName,
737
+ styles: {
738
+ root: {
739
+ fontSize: isActive ? activeSize : baseSize,
740
+ color: isActive ? "#ffffff" : outlineColor,
741
+ opacity: disabled ? 0.4 : selectedIndex !== null && !isActive ? 0.4 : 1,
742
+ transition: "all 0.15s ease",
743
+ display: "flex"
744
+ }
745
+ }
746
+ }
747
+ )
748
+ },
749
+ iconName
750
+ );
751
+ })
752
+ }
753
+ );
754
+ };
755
+
756
+ // src/v8/controls/rating.tsx
757
+ var import_jsx_runtime14 = require("react/jsx-runtime");
758
+ var RatingControl = ({ control, interactive }) => {
759
+ const disabled = isDisabled(control, interactive);
760
+ const max = control.properties.max ?? 5;
761
+ const allowZeroStars = control.properties.allowZeroStars ?? false;
762
+ const size = control.properties.size ?? "medium";
763
+ const ratingStyle = control.properties.ratingStyle ?? "stars";
764
+ const useColor = control.properties.useColor ?? false;
765
+ const starColor = control.properties.starColor ?? "#ffc83d";
766
+ const customEmojiColor = control.properties.emojiColor || "";
767
+ if (ratingStyle === "stars") {
768
+ const sizeMap = {
769
+ small: import_react14.RatingSize.Small,
770
+ medium: import_react14.RatingSize.Small,
771
+ large: import_react14.RatingSize.Large
772
+ };
773
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
774
+ import_react14.Rating,
775
+ {
776
+ max,
777
+ allowZeroStars,
778
+ size: sizeMap[size] ?? import_react14.RatingSize.Small,
779
+ disabled,
780
+ styles: useColor ? {
781
+ ratingStarFront: { color: starColor },
782
+ ratingStarBack: { color: "#c8c6c4" }
783
+ } : void 0
784
+ }
785
+ );
786
+ }
787
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
788
+ EmojiRating,
789
+ {
790
+ max,
791
+ size,
792
+ useColor,
793
+ customEmojiColor,
794
+ disabled
795
+ }
796
+ );
797
+ };
798
+
799
+ // src/v8/controls/spinbutton.tsx
800
+ var import_react15 = require("@fluentui/react");
801
+ var import_jsx_runtime15 = require("react/jsx-runtime");
802
+ var SpinButtonControl = ({ control, interactive }) => {
803
+ const disabled = isDisabled(control, interactive);
804
+ const min = control.properties.min ?? 0;
805
+ const max = control.properties.max ?? 100;
806
+ const step = control.properties.step ?? 1;
807
+ const precision = control.properties.precision ?? 0;
808
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
809
+ import_react15.SpinButton,
810
+ {
811
+ min,
812
+ max,
813
+ step,
814
+ precision,
815
+ disabled,
816
+ styles: { root: { width: "100%" } }
817
+ }
818
+ );
819
+ };
820
+
821
+ // src/v8/controls/dropdown.tsx
822
+ var import_react16 = require("@fluentui/react");
823
+ var import_jsx_runtime16 = require("react/jsx-runtime");
824
+ function resolveRawOptions(optionSourceMode, customOptions, metadataOptions) {
825
+ if (optionSourceMode === "custom" && customOptions && customOptions.length > 0) {
826
+ return customOptions;
827
+ }
828
+ if (metadataOptions && metadataOptions.length > 0) {
829
+ return metadataOptions.map((opt) => ({ key: opt.value, text: opt.label, color: opt.color }));
830
+ }
831
+ return customOptions ?? [];
832
+ }
833
+ var DropdownControl = ({
834
+ control,
835
+ effectiveValue,
836
+ displayValue,
837
+ hasValue,
838
+ interactive,
839
+ onChange
840
+ }) => {
841
+ const disabled = isDisabled(control, interactive);
842
+ const optionSourceMode = control.properties.optionSourceMode ?? "custom";
843
+ const metadataOptions = control.dataBinding?.optionSetOptions;
844
+ const customOptions = control.properties.options;
845
+ const isMultiSelect = control.properties.multiSelect ?? false;
846
+ const showOptionColors = control.properties.showOptionColors ?? false;
847
+ const showPlaceholder = control.properties.showPlaceholder ?? false;
848
+ const customPlaceholder = control.properties.placeholder;
849
+ const placeholder = showPlaceholder ? customPlaceholder ?? (isMultiSelect ? "Select options" : "Select an option") : void 0;
850
+ const rawOptions = resolveRawOptions(optionSourceMode, customOptions, metadataOptions);
851
+ const options = rawOptions.map((opt) => ({
852
+ key: opt.key,
853
+ text: opt.text,
854
+ data: { color: opt.color, icon: opt.icon }
855
+ }));
856
+ let selectedKey;
857
+ let selectedKeys = [];
858
+ if (hasValue) {
859
+ if (isMultiSelect && Array.isArray(effectiveValue)) {
860
+ selectedKeys = effectiveValue;
861
+ } else if (typeof effectiveValue === "number") {
862
+ selectedKey = effectiveValue;
863
+ selectedKeys = [effectiveValue];
864
+ } else if (typeof effectiveValue === "string") {
865
+ const matchByText = options.find((opt) => opt.text.toLowerCase() === displayValue.toLowerCase());
866
+ selectedKey = matchByText?.key ?? effectiveValue;
867
+ selectedKeys = matchByText ? [matchByText.key] : [];
868
+ }
869
+ }
870
+ const renderOption = (optProps) => {
871
+ if (!optProps) return null;
872
+ const optColor = optProps.data?.color;
873
+ const optIcon = optProps.data?.icon;
874
+ const iconColor = showOptionColors && optColor ? optColor : "#605e5c";
875
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
876
+ optIcon ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react16.Icon, { iconName: optIcon, styles: { root: { fontSize: 14, color: iconColor, flexShrink: 0 } } }) : showOptionColors && optColor && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
877
+ "div",
878
+ {
879
+ style: {
880
+ width: 12,
881
+ height: 12,
882
+ borderRadius: 2,
883
+ backgroundColor: optColor,
884
+ flexShrink: 0
885
+ }
886
+ }
887
+ ),
888
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { children: optProps.text })
889
+ ] });
890
+ };
891
+ const renderTitle = (titleOptions) => {
892
+ if (!titleOptions || titleOptions.length === 0) return null;
893
+ if (isMultiSelect) {
894
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { display: "flex", alignItems: "center", gap: 4, flexWrap: "wrap" }, children: titleOptions.map((opt2) => {
895
+ const optIcon2 = opt2.data?.icon;
896
+ const optColor2 = opt2.data?.color;
897
+ const iconColor2 = showOptionColors && optColor2 ? optColor2 : "#605e5c";
898
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
899
+ "span",
900
+ {
901
+ style: {
902
+ display: "inline-flex",
903
+ alignItems: "center",
904
+ gap: 4,
905
+ backgroundColor: "#f3f2f1",
906
+ padding: "2px 6px",
907
+ borderRadius: 3,
908
+ fontSize: 12
909
+ },
910
+ children: [
911
+ optIcon2 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react16.Icon, { iconName: optIcon2, styles: { root: { fontSize: 12, color: iconColor2 } } }) : showOptionColors && optColor2 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
912
+ "div",
913
+ {
914
+ style: {
915
+ width: 8,
916
+ height: 8,
917
+ borderRadius: 2,
918
+ backgroundColor: optColor2
919
+ }
920
+ }
921
+ ),
922
+ opt2.text
923
+ ]
924
+ },
925
+ opt2.key
926
+ );
927
+ }) });
928
+ }
929
+ const opt = titleOptions[0];
930
+ const optIcon = opt.data?.icon;
931
+ const optColor = opt.data?.color;
932
+ const iconColor = showOptionColors && optColor ? optColor : "#605e5c";
933
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
934
+ optIcon ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react16.Icon, { iconName: optIcon, styles: { root: { fontSize: 14, color: iconColor } } }) : showOptionColors && optColor && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
935
+ "div",
936
+ {
937
+ style: {
938
+ width: 12,
939
+ height: 12,
940
+ borderRadius: 2,
941
+ backgroundColor: optColor
942
+ }
943
+ }
944
+ ),
945
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { children: opt.text })
946
+ ] });
947
+ };
948
+ if (isMultiSelect) {
949
+ const comboOptions = options.map((opt) => ({
950
+ ...opt,
951
+ selected: selectedKeys.includes(opt.key)
952
+ }));
953
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
954
+ import_react16.ComboBox,
955
+ {
956
+ placeholder,
957
+ options: comboOptions,
958
+ disabled,
959
+ multiSelect: true,
960
+ selectedKey: selectedKeys,
961
+ onRenderOption: renderOption,
962
+ onChange: onChange ? (_, option) => {
963
+ if (!option) return;
964
+ const key = option.key;
965
+ const newKeys = option.selected ? [...selectedKeys, key] : selectedKeys.filter((k) => k !== key);
966
+ onChange(newKeys.join(","));
967
+ } : void 0
968
+ }
969
+ );
970
+ }
971
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
972
+ import_react16.Dropdown,
973
+ {
974
+ placeholder,
975
+ options,
976
+ disabled,
977
+ selectedKey,
978
+ onRenderOption: renderOption,
979
+ onRenderTitle: renderTitle,
980
+ onChange: onChange ? (_, option) => onChange(option?.key ?? "") : void 0
981
+ }
982
+ );
983
+ };
984
+
985
+ // src/v8/controls/combobox.tsx
986
+ var import_react17 = require("@fluentui/react");
987
+ var import_jsx_runtime17 = require("react/jsx-runtime");
988
+ function resolveRawOptions2(mode, customOptions, metadataOptions) {
989
+ if (mode === "custom" && customOptions && customOptions.length > 0) {
990
+ return customOptions;
991
+ }
992
+ if (metadataOptions && metadataOptions.length > 0) {
993
+ return metadataOptions.map((opt) => ({ key: opt.value, text: opt.label, color: opt.color }));
994
+ }
995
+ return customOptions ?? [];
996
+ }
997
+ var ComboBoxControl = ({
998
+ control,
999
+ effectiveValue,
1000
+ displayValue,
1001
+ hasValue,
1002
+ interactive,
1003
+ onChange
1004
+ }) => {
1005
+ const disabled = isDisabled(control, interactive);
1006
+ const sourceMode = control.properties.optionSourceMode ?? "custom";
1007
+ const metadataOptions = control.dataBinding?.optionSetOptions;
1008
+ const customOptions = control.properties.options;
1009
+ const multiSelect = control.properties.multiSelect ?? false;
1010
+ const allowFreeform = control.properties.allowFreeform ?? true;
1011
+ const autoComplete = control.properties.autoComplete ?? "on";
1012
+ const showOptionColors = control.properties.showOptionColors ?? false;
1013
+ const showPlaceholder = control.properties.showPlaceholder ?? false;
1014
+ const customPlaceholder = control.properties.placeholder;
1015
+ const placeholder = showPlaceholder ? customPlaceholder ?? (multiSelect ? "Select options" : "Search or select an option") : void 0;
1016
+ const rawOptions = resolveRawOptions2(sourceMode, customOptions, metadataOptions);
1017
+ const options = rawOptions.map((opt) => ({
1018
+ key: opt.key,
1019
+ text: opt.text,
1020
+ data: { color: opt.color, icon: opt.icon },
1021
+ ...opt.itemType === "header" ? { itemType: 2 } : {},
1022
+ ...opt.itemType === "divider" ? { itemType: 1 } : {}
1023
+ }));
1024
+ let selectedKey;
1025
+ let selectedKeys = [];
1026
+ if (hasValue) {
1027
+ if (multiSelect && Array.isArray(effectiveValue)) {
1028
+ selectedKeys = effectiveValue;
1029
+ } else if (typeof effectiveValue === "number") {
1030
+ selectedKey = effectiveValue;
1031
+ selectedKeys = [effectiveValue];
1032
+ } else if (typeof effectiveValue === "string") {
1033
+ const matchByText = options.find((opt) => opt.text.toLowerCase() === displayValue.toLowerCase());
1034
+ selectedKey = matchByText?.key ?? effectiveValue;
1035
+ selectedKeys = matchByText ? [matchByText.key] : [];
1036
+ }
1037
+ }
1038
+ const renderOption = (optProps) => {
1039
+ if (!optProps) return null;
1040
+ const optColor = optProps.data?.color;
1041
+ const optIcon = optProps.data?.icon;
1042
+ const iconColor = showOptionColors && optColor ? optColor : "#605e5c";
1043
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
1044
+ optIcon ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react17.Icon, { iconName: optIcon, styles: { root: { fontSize: 14, color: iconColor, flexShrink: 0 } } }) : showOptionColors && optColor && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1045
+ "div",
1046
+ {
1047
+ style: {
1048
+ width: 12,
1049
+ height: 12,
1050
+ borderRadius: 2,
1051
+ backgroundColor: optColor,
1052
+ flexShrink: 0
1053
+ }
1054
+ }
1055
+ ),
1056
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { children: optProps.text })
1057
+ ] });
1058
+ };
1059
+ if (multiSelect) {
1060
+ const showSelectAll = control.properties.showSelectAll ?? false;
1061
+ const selectableOptions = options.filter((opt) => opt.itemType !== 2 && opt.itemType !== 1);
1062
+ const multiOptions = options.map((opt) => ({
1063
+ ...opt,
1064
+ selected: selectedKeys.includes(opt.key)
1065
+ }));
1066
+ const allSelectableKeys = selectableOptions.map((opt) => opt.key);
1067
+ const allSelected = allSelectableKeys.length > 0 && allSelectableKeys.every((k) => selectedKeys.includes(k));
1068
+ if (showSelectAll) {
1069
+ multiOptions.unshift({
1070
+ key: "selectAll",
1071
+ text: "Select All",
1072
+ styles: { optionText: { fontWeight: 600 } },
1073
+ selected: allSelected
1074
+ });
1075
+ }
1076
+ const multiOnChange = onChange ? (_, option) => {
1077
+ if (!option) return;
1078
+ if (option.key === "selectAll") {
1079
+ const newKeys2 = option.selected ? allSelectableKeys : [];
1080
+ onChange(newKeys2.join(","));
1081
+ return;
1082
+ }
1083
+ const key = option.key;
1084
+ const newKeys = option.selected ? [...selectedKeys, key] : selectedKeys.filter((k) => k !== key);
1085
+ onChange(newKeys.join(","));
1086
+ } : () => {
1087
+ };
1088
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1089
+ import_react17.ComboBox,
1090
+ {
1091
+ placeholder,
1092
+ options: multiOptions,
1093
+ disabled,
1094
+ multiSelect: true,
1095
+ allowFreeform,
1096
+ autoComplete,
1097
+ onRenderOption: renderOption,
1098
+ onChange: multiOnChange
1099
+ }
1100
+ );
1101
+ }
1102
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1103
+ import_react17.ComboBox,
1104
+ {
1105
+ placeholder,
1106
+ options,
1107
+ disabled,
1108
+ allowFreeform,
1109
+ autoComplete,
1110
+ selectedKey,
1111
+ onRenderOption: renderOption,
1112
+ onChange: onChange ? (_, option) => onChange(option?.key ?? "") : void 0
1113
+ }
1114
+ );
1115
+ };
1116
+
1117
+ // src/v8/controls/internals/ChoiceGroupControl.tsx
1118
+ var import_react18 = require("react");
1119
+ var import_react19 = require("@fluentui/react");
1120
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1121
+ var ChoiceGroupControl = ({
1122
+ options: rawOptions,
1123
+ showColors,
1124
+ showLabels,
1125
+ layout,
1126
+ disabled,
1127
+ initialSelectedKey,
1128
+ onChange
1129
+ }) => {
1130
+ const [selectedKey, setSelectedKey] = (0, import_react18.useState)(initialSelectedKey);
1131
+ const handleSelect = (key) => {
1132
+ if (disabled) return;
1133
+ const keyStr = String(key);
1134
+ setSelectedKey(keyStr);
1135
+ if (onChange) onChange(key);
1136
+ };
1137
+ if (layout === "icon-cards") {
1138
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { role: "radiogroup", style: { display: "flex", flexWrap: "wrap", gap: 8 }, children: rawOptions.map((opt) => {
1139
+ const optKey = String(opt.key);
1140
+ const isSelected = selectedKey === optKey;
1141
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
1142
+ "div",
1143
+ {
1144
+ role: "radio",
1145
+ "aria-checked": isSelected,
1146
+ tabIndex: 0,
1147
+ onClick: () => handleSelect(opt.key),
1148
+ onKeyDown: (e) => {
1149
+ if (e.key === "Enter" || e.key === " ") {
1150
+ e.preventDefault();
1151
+ handleSelect(opt.key);
1152
+ }
1153
+ },
1154
+ style: {
1155
+ minWidth: 100,
1156
+ border: isSelected ? "2px solid #0078d4" : "1px solid #edebe9",
1157
+ borderRadius: 4,
1158
+ padding: 12,
1159
+ display: "flex",
1160
+ flexDirection: "column",
1161
+ alignItems: "center",
1162
+ gap: 8,
1163
+ cursor: disabled ? "default" : "pointer",
1164
+ opacity: disabled ? 0.5 : 1,
1165
+ backgroundColor: isSelected ? "#f0f6ff" : "#ffffff",
1166
+ position: "relative",
1167
+ transition: "background-color 0.15s, border-color 0.15s"
1168
+ },
1169
+ onMouseEnter: (e) => {
1170
+ if (!disabled && !isSelected) e.currentTarget.style.backgroundColor = "#f3f2f1";
1171
+ },
1172
+ onMouseLeave: (e) => {
1173
+ if (!isSelected) e.currentTarget.style.backgroundColor = "#ffffff";
1174
+ },
1175
+ children: [
1176
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1177
+ "div",
1178
+ {
1179
+ style: {
1180
+ position: "absolute",
1181
+ top: 8,
1182
+ left: 8,
1183
+ width: 16,
1184
+ height: 16,
1185
+ borderRadius: "50%",
1186
+ border: `1px solid ${isSelected ? "#0078d4" : "#605e5c"}`,
1187
+ display: "flex",
1188
+ alignItems: "center",
1189
+ justifyContent: "center"
1190
+ },
1191
+ children: isSelected && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1192
+ "div",
1193
+ {
1194
+ style: {
1195
+ width: 8,
1196
+ height: 8,
1197
+ borderRadius: "50%",
1198
+ backgroundColor: "#0078d4"
1199
+ }
1200
+ }
1201
+ )
1202
+ }
1203
+ ),
1204
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1205
+ import_react19.Icon,
1206
+ {
1207
+ iconName: opt.icon || "RadioBtnOn",
1208
+ styles: {
1209
+ root: {
1210
+ fontSize: 32,
1211
+ color: showColors && opt.color ? opt.color : isSelected ? "#0078d4" : "#605e5c",
1212
+ marginTop: 8
1213
+ }
1214
+ }
1215
+ }
1216
+ ),
1217
+ showLabels && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: { fontSize: 12, color: "#323130", textAlign: "center" }, children: opt.text })
1218
+ ]
1219
+ },
1220
+ optKey
1221
+ );
1222
+ }) });
1223
+ }
1224
+ if (layout === "image-cards") {
1225
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { role: "radiogroup", style: { display: "flex", flexWrap: "wrap", gap: 8 }, children: rawOptions.map((opt) => {
1226
+ const optKey = String(opt.key);
1227
+ const isSelected = selectedKey === optKey;
1228
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
1229
+ "div",
1230
+ {
1231
+ role: "radio",
1232
+ "aria-checked": isSelected,
1233
+ tabIndex: 0,
1234
+ onClick: () => handleSelect(opt.key),
1235
+ onKeyDown: (e) => {
1236
+ if (e.key === "Enter" || e.key === " ") {
1237
+ e.preventDefault();
1238
+ handleSelect(opt.key);
1239
+ }
1240
+ },
1241
+ style: {
1242
+ minWidth: 100,
1243
+ border: isSelected ? "2px solid #0078d4" : "1px solid #edebe9",
1244
+ borderRadius: 4,
1245
+ padding: 12,
1246
+ display: "flex",
1247
+ flexDirection: "column",
1248
+ alignItems: "center",
1249
+ gap: 8,
1250
+ cursor: disabled ? "default" : "pointer",
1251
+ opacity: disabled ? 0.5 : 1,
1252
+ backgroundColor: isSelected ? "#f0f6ff" : "#ffffff",
1253
+ position: "relative",
1254
+ transition: "background-color 0.15s, border-color 0.15s"
1255
+ },
1256
+ onMouseEnter: (e) => {
1257
+ if (!disabled && !isSelected) e.currentTarget.style.backgroundColor = "#f3f2f1";
1258
+ },
1259
+ onMouseLeave: (e) => {
1260
+ if (!isSelected) e.currentTarget.style.backgroundColor = "#ffffff";
1261
+ },
1262
+ children: [
1263
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1264
+ "div",
1265
+ {
1266
+ style: {
1267
+ position: "absolute",
1268
+ top: 8,
1269
+ left: 8,
1270
+ width: 16,
1271
+ height: 16,
1272
+ borderRadius: "50%",
1273
+ border: `1px solid ${isSelected ? "#0078d4" : "#605e5c"}`,
1274
+ display: "flex",
1275
+ alignItems: "center",
1276
+ justifyContent: "center"
1277
+ },
1278
+ children: isSelected && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1279
+ "div",
1280
+ {
1281
+ style: {
1282
+ width: 8,
1283
+ height: 8,
1284
+ borderRadius: "50%",
1285
+ backgroundColor: "#0078d4"
1286
+ }
1287
+ }
1288
+ )
1289
+ }
1290
+ ),
1291
+ opt.imageSrc ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1292
+ "img",
1293
+ {
1294
+ src: opt.imageSrc,
1295
+ alt: opt.text,
1296
+ style: { width: 60, height: 60, objectFit: "contain", marginTop: 8 }
1297
+ }
1298
+ ) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1299
+ import_react19.Icon,
1300
+ {
1301
+ iconName: "Photo2",
1302
+ styles: { root: { fontSize: 32, color: "#a19f9d", marginTop: 8 } }
1303
+ }
1304
+ ),
1305
+ showLabels && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: { fontSize: 12, color: "#323130", textAlign: "center" }, children: opt.text })
1306
+ ]
1307
+ },
1308
+ optKey
1309
+ );
1310
+ }) });
1311
+ }
1312
+ const hasDecorations = !showLabels || rawOptions.some((opt) => opt.icon || showColors && opt.color);
1313
+ if (hasDecorations) {
1314
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1315
+ "div",
1316
+ {
1317
+ role: "radiogroup",
1318
+ style: {
1319
+ display: "flex",
1320
+ flexDirection: layout === "horizontal" ? "row" : "column",
1321
+ gap: layout === "horizontal" ? 16 : 0
1322
+ },
1323
+ children: rawOptions.map((opt) => {
1324
+ const optKey = String(opt.key);
1325
+ const isSelected = selectedKey === optKey;
1326
+ const optIcon = opt.icon;
1327
+ const optColor = showColors ? opt.color : void 0;
1328
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
1329
+ "div",
1330
+ {
1331
+ role: "radio",
1332
+ "aria-checked": isSelected,
1333
+ tabIndex: 0,
1334
+ onClick: () => handleSelect(opt.key),
1335
+ onKeyDown: (e) => {
1336
+ if (e.key === "Enter" || e.key === " ") {
1337
+ e.preventDefault();
1338
+ handleSelect(opt.key);
1339
+ }
1340
+ },
1341
+ style: {
1342
+ display: "flex",
1343
+ alignItems: "center",
1344
+ gap: 8,
1345
+ padding: "4px 0",
1346
+ cursor: disabled ? "default" : "pointer",
1347
+ opacity: disabled ? 0.5 : 1
1348
+ },
1349
+ children: [
1350
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1351
+ "div",
1352
+ {
1353
+ style: {
1354
+ width: 20,
1355
+ height: 20,
1356
+ borderRadius: "50%",
1357
+ border: `1px solid ${isSelected ? "#0078d4" : "#605e5c"}`,
1358
+ display: "flex",
1359
+ alignItems: "center",
1360
+ justifyContent: "center",
1361
+ flexShrink: 0
1362
+ },
1363
+ children: isSelected && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1364
+ "div",
1365
+ {
1366
+ style: {
1367
+ width: 10,
1368
+ height: 10,
1369
+ borderRadius: "50%",
1370
+ backgroundColor: "#0078d4"
1371
+ }
1372
+ }
1373
+ )
1374
+ }
1375
+ ),
1376
+ optIcon && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1377
+ import_react19.Icon,
1378
+ {
1379
+ iconName: optIcon,
1380
+ styles: {
1381
+ root: {
1382
+ fontSize: 14,
1383
+ color: optColor || "#605e5c",
1384
+ flexShrink: 0
1385
+ }
1386
+ }
1387
+ }
1388
+ ),
1389
+ !optIcon && optColor && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1390
+ "div",
1391
+ {
1392
+ style: {
1393
+ width: 12,
1394
+ height: 12,
1395
+ borderRadius: 2,
1396
+ backgroundColor: optColor,
1397
+ flexShrink: 0
1398
+ }
1399
+ }
1400
+ ),
1401
+ showLabels && opt.text && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: { fontSize: 14, color: "#323130" }, children: opt.text })
1402
+ ]
1403
+ },
1404
+ optKey
1405
+ );
1406
+ })
1407
+ }
1408
+ );
1409
+ }
1410
+ const cgOptions = rawOptions.map((opt) => ({
1411
+ key: String(opt.key),
1412
+ text: opt.text
1413
+ }));
1414
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1415
+ import_react19.ChoiceGroup,
1416
+ {
1417
+ options: cgOptions,
1418
+ selectedKey,
1419
+ disabled,
1420
+ onChange: (_, option) => {
1421
+ if (option) handleSelect(Number(option.key));
1422
+ },
1423
+ styles: layout === "horizontal" ? {
1424
+ flexContainer: { display: "flex", flexDirection: "row", gap: 16 }
1425
+ } : void 0
1426
+ }
1427
+ );
1428
+ };
1429
+
1430
+ // src/v8/controls/choicegroup.tsx
1431
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1432
+ var ChoiceGroupBranch = ({
1433
+ control,
1434
+ effectiveValue,
1435
+ hasValue,
1436
+ interactive,
1437
+ onChange
1438
+ }) => {
1439
+ const disabled = isDisabled(control, interactive);
1440
+ const sourceMode = control.properties.optionSourceMode ?? "custom";
1441
+ const metadataOptions = control.dataBinding?.optionSetOptions;
1442
+ const customOptions = control.properties.options;
1443
+ let rawOptions;
1444
+ if (sourceMode === "custom" && customOptions && customOptions.length > 0) {
1445
+ rawOptions = customOptions;
1446
+ } else if (metadataOptions && metadataOptions.length > 0) {
1447
+ rawOptions = metadataOptions.map((opt) => ({ key: opt.value, text: opt.label, color: opt.color }));
1448
+ } else {
1449
+ rawOptions = customOptions ?? [];
1450
+ }
1451
+ let selectedKey;
1452
+ if (hasValue) {
1453
+ if (typeof effectiveValue === "number") {
1454
+ selectedKey = String(effectiveValue);
1455
+ } else if (typeof effectiveValue === "string") {
1456
+ selectedKey = effectiveValue;
1457
+ }
1458
+ }
1459
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1460
+ ChoiceGroupControl,
1461
+ {
1462
+ options: rawOptions,
1463
+ showColors: control.properties.showOptionColors ?? false,
1464
+ showLabels: control.properties.showLabels ?? true,
1465
+ layout: control.properties.layout ?? "vertical",
1466
+ disabled,
1467
+ initialSelectedKey: selectedKey,
1468
+ onChange
1469
+ }
1470
+ );
1471
+ };
1472
+
1473
+ // src/v8/controls/internals/LookupControl.tsx
1474
+ var import_react21 = require("react");
1475
+ var import_react22 = require("@fluentui/react");
1476
+
1477
+ // src/context/FormRuntimeContext.tsx
1478
+ var import_react20 = require("react");
1479
+ var import_jsx_runtime20 = require("react/jsx-runtime");
1480
+ var FormRuntimeContext = (0, import_react20.createContext)(null);
1481
+ FormRuntimeContext.displayName = "FormRuntimeContext";
1482
+ function useOptionalFormRuntimeContext() {
1483
+ return (0, import_react20.useContext)(FormRuntimeContext);
1484
+ }
1485
+
1486
+ // src/v8/controls/internals/LookupControl.tsx
1487
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1488
+ var containerClass2 = (0, import_react22.mergeStyles)({
1489
+ position: "relative",
1490
+ display: "flex",
1491
+ alignItems: "center",
1492
+ border: "1px solid #8a8886",
1493
+ borderRadius: 2,
1494
+ backgroundColor: "#ffffff",
1495
+ minHeight: 32,
1496
+ ":hover": {
1497
+ borderColor: "#323130"
1498
+ },
1499
+ ":focus-within": {
1500
+ borderColor: "#0078d4",
1501
+ borderWidth: 2
1502
+ }
1503
+ });
1504
+ var containerDisabledClass = (0, import_react22.mergeStyles)({
1505
+ backgroundColor: "#f3f2f1",
1506
+ borderColor: "#c8c6c4",
1507
+ ":hover": {
1508
+ borderColor: "#c8c6c4"
1509
+ }
1510
+ });
1511
+ var inputWrapperClass = (0, import_react22.mergeStyles)({
1512
+ flex: 1,
1513
+ display: "flex",
1514
+ alignItems: "center",
1515
+ minWidth: 0,
1516
+ padding: "0 8px",
1517
+ gap: 6
1518
+ });
1519
+ var inputClass = (0, import_react22.mergeStyles)({
1520
+ flex: 1,
1521
+ border: "none",
1522
+ outline: "none",
1523
+ backgroundColor: "transparent",
1524
+ fontSize: 14,
1525
+ color: "#323130",
1526
+ padding: "6px 0",
1527
+ minWidth: 0,
1528
+ "::placeholder": {
1529
+ color: "#605e5c"
1530
+ }
1531
+ });
1532
+ var searchButtonClass = (0, import_react22.mergeStyles)({
1533
+ borderLeft: "1px solid #edebe9",
1534
+ height: "100%",
1535
+ display: "flex",
1536
+ alignItems: "center",
1537
+ justifyContent: "center",
1538
+ padding: "0 8px",
1539
+ cursor: "pointer",
1540
+ backgroundColor: "transparent",
1541
+ ":hover": {
1542
+ backgroundColor: "#f3f2f1"
1543
+ }
1544
+ });
1545
+ var selectedValueClass = (0, import_react22.mergeStyles)({
1546
+ display: "flex",
1547
+ alignItems: "center",
1548
+ gap: 6,
1549
+ flex: 1,
1550
+ minWidth: 0,
1551
+ padding: "4px 0"
1552
+ });
1553
+ var selectedLinkClass = (0, import_react22.mergeStyles)({
1554
+ flex: 1,
1555
+ minWidth: 0,
1556
+ overflow: "hidden",
1557
+ textOverflow: "ellipsis",
1558
+ whiteSpace: "nowrap",
1559
+ fontSize: 14,
1560
+ color: "#0078d4",
1561
+ textDecoration: "underline",
1562
+ cursor: "pointer",
1563
+ ":hover": {
1564
+ color: "#004578"
1565
+ }
1566
+ });
1567
+ var clearButtonClass = (0, import_react22.mergeStyles)({
1568
+ padding: 4,
1569
+ cursor: "pointer",
1570
+ display: "flex",
1571
+ alignItems: "center",
1572
+ justifyContent: "center",
1573
+ ":hover": {
1574
+ backgroundColor: "#f3f2f1",
1575
+ borderRadius: 2
1576
+ }
1577
+ });
1578
+ var calloutClass = (0, import_react22.mergeStyles)({
1579
+ display: "flex",
1580
+ flexDirection: "column",
1581
+ backgroundColor: "#ffffff",
1582
+ boxShadow: "rgba(0, 0, 0, 0.12) 0px 0px 2px, rgba(0, 0, 0, 0.14) 0px 2px 4px",
1583
+ borderRadius: 2,
1584
+ overflow: "hidden"
1585
+ });
1586
+ var searchHeaderClass = (0, import_react22.mergeStyles)({
1587
+ padding: "8px 12px",
1588
+ borderBottom: "1px solid #edebe9",
1589
+ display: "flex",
1590
+ alignItems: "center",
1591
+ gap: 8
1592
+ });
1593
+ var searchInputClass = (0, import_react22.mergeStyles)({
1594
+ flex: 1,
1595
+ border: "none",
1596
+ outline: "none",
1597
+ fontSize: 14,
1598
+ color: "#323130",
1599
+ "::placeholder": {
1600
+ color: "#605e5c"
1601
+ }
1602
+ });
1603
+ var resultsFromClass = (0, import_react22.mergeStyles)({
1604
+ padding: "8px 12px",
1605
+ borderBottom: "1px solid #edebe9",
1606
+ display: "flex",
1607
+ alignItems: "center",
1608
+ gap: 8,
1609
+ fontSize: 12,
1610
+ color: "#605e5c"
1611
+ });
1612
+ var filterTabClass = (0, import_react22.mergeStyles)({
1613
+ padding: "4px 10px",
1614
+ border: "1px solid #8a8886",
1615
+ borderRadius: 2,
1616
+ backgroundColor: "#ffffff",
1617
+ fontSize: 12,
1618
+ cursor: "pointer",
1619
+ ":hover": {
1620
+ backgroundColor: "#f3f2f1"
1621
+ }
1622
+ });
1623
+ var filterTabActiveClass = (0, import_react22.mergeStyles)({
1624
+ backgroundColor: "#f3f2f1",
1625
+ borderColor: "#0078d4"
1626
+ });
1627
+ var listContainerClass = (0, import_react22.mergeStyles)({
1628
+ flex: 1,
1629
+ overflowY: "auto",
1630
+ maxHeight: 200
1631
+ });
1632
+ var recordItemClass = (0, import_react22.mergeStyles)({
1633
+ display: "flex",
1634
+ alignItems: "center",
1635
+ padding: "8px 12px",
1636
+ cursor: "pointer",
1637
+ backgroundColor: "transparent",
1638
+ border: "none",
1639
+ width: "100%",
1640
+ textAlign: "left",
1641
+ gap: 10,
1642
+ ":hover": {
1643
+ backgroundColor: "#f3f2f1"
1644
+ },
1645
+ ":focus": {
1646
+ backgroundColor: "#f3f2f1",
1647
+ outline: "none"
1648
+ }
1649
+ });
1650
+ var entityIconClass = (0, import_react22.mergeStyles)({
1651
+ fontSize: 16,
1652
+ color: "#605e5c",
1653
+ width: 20,
1654
+ display: "flex",
1655
+ alignItems: "center",
1656
+ justifyContent: "center"
1657
+ });
1658
+ var recordNameClass = (0, import_react22.mergeStyles)({
1659
+ fontSize: 14,
1660
+ color: "#323130",
1661
+ flex: 1,
1662
+ overflow: "hidden",
1663
+ textOverflow: "ellipsis",
1664
+ whiteSpace: "nowrap"
1665
+ });
1666
+ var noResultsClass = (0, import_react22.mergeStyles)({
1667
+ padding: 16,
1668
+ textAlign: "center",
1669
+ color: "#605e5c",
1670
+ fontSize: 13
1671
+ });
1672
+ var loadingClass = (0, import_react22.mergeStyles)({
1673
+ padding: 16,
1674
+ textAlign: "center"
1675
+ });
1676
+ var footerClass = (0, import_react22.mergeStyles)({
1677
+ display: "flex",
1678
+ alignItems: "center",
1679
+ justifyContent: "space-between",
1680
+ padding: "8px 12px",
1681
+ borderTop: "1px solid #edebe9",
1682
+ backgroundColor: "#faf9f8"
1683
+ });
1684
+ var footerButtonClass = (0, import_react22.mergeStyles)({
1685
+ display: "flex",
1686
+ alignItems: "center",
1687
+ gap: 6,
1688
+ padding: "4px 8px",
1689
+ border: "none",
1690
+ backgroundColor: "transparent",
1691
+ cursor: "pointer",
1692
+ fontSize: 13,
1693
+ color: "#323130",
1694
+ ":hover": {
1695
+ backgroundColor: "#edebe9",
1696
+ borderRadius: 2
1697
+ }
1698
+ });
1699
+ var emptyValueClass = (0, import_react22.mergeStyles)({
1700
+ color: "#605e5c",
1701
+ fontSize: 14,
1702
+ padding: "6px 0"
1703
+ });
1704
+ function defaultResolveEntitySetName(entityLogicalName) {
1705
+ return entityLogicalName + "s";
1706
+ }
1707
+ function getPrimaryNameAttribute(entityLogicalName) {
1708
+ const knownPrimaryNames = {
1709
+ account: "name",
1710
+ contact: "fullname",
1711
+ lead: "fullname",
1712
+ opportunity: "name",
1713
+ incident: "title",
1714
+ systemuser: "fullname",
1715
+ team: "name"
1716
+ };
1717
+ return knownPrimaryNames[entityLogicalName] || "name";
1718
+ }
1719
+ function getInitials(name) {
1720
+ if (!name) return "?";
1721
+ const words = name.trim().split(/\s+/);
1722
+ if (words.length >= 2) {
1723
+ return (words[0][0] + words[1][0]).toUpperCase();
1724
+ }
1725
+ return words[0].substring(0, 2).toUpperCase();
1726
+ }
1727
+ function getEntityIcon(entityLogicalName) {
1728
+ const iconMap = {
1729
+ account: "CityNext",
1730
+ contact: "Contact",
1731
+ lead: "People",
1732
+ opportunity: "Money",
1733
+ incident: "Ticket",
1734
+ systemuser: "Contact",
1735
+ team: "Group"
1736
+ };
1737
+ return iconMap[entityLogicalName] || "Database";
1738
+ }
1739
+ var PERSON_ENTITIES = /* @__PURE__ */ new Set(["systemuser", "contact", "lead"]);
1740
+ function isPersonEntity(entityLogicalName) {
1741
+ return !!entityLogicalName && PERSON_ENTITIES.has(entityLogicalName);
1742
+ }
1743
+ function getSecondaryTextField(entityLogicalName) {
1744
+ const map = {
1745
+ systemuser: "jobtitle",
1746
+ contact: "jobtitle",
1747
+ account: "description",
1748
+ team: "description",
1749
+ lead: "jobtitle"
1750
+ };
1751
+ return map[entityLogicalName] || null;
1752
+ }
1753
+ function derivePresence(record) {
1754
+ if (record.entityType === "systemuser") {
1755
+ return record.isDisabled ? import_react22.PersonaPresence.offline : import_react22.PersonaPresence.online;
1756
+ }
1757
+ if (isPersonEntity(record.entityType || null)) {
1758
+ return import_react22.PersonaPresence.none;
1759
+ }
1760
+ return import_react22.PersonaPresence.none;
1761
+ }
1762
+ var LookupControl = ({
1763
+ control,
1764
+ disabled = false,
1765
+ value,
1766
+ onChange,
1767
+ searchLookup: searchLookupProp,
1768
+ isConnectedOverride,
1769
+ fetchEntityImage,
1770
+ resolveEntitySetName = defaultResolveEntitySetName
1771
+ }) => {
1772
+ const [selectedRecord, setSelectedRecord] = (0, import_react21.useState)(null);
1773
+ const [searchText, setSearchText] = (0, import_react21.useState)("");
1774
+ const [isOpen, setIsOpen] = (0, import_react21.useState)(false);
1775
+ const [records, setRecords] = (0, import_react21.useState)([]);
1776
+ const [loading, setLoading] = (0, import_react21.useState)(false);
1777
+ const [error, setError] = (0, import_react21.useState)(null);
1778
+ const containerRef = (0, import_react21.useRef)(null);
1779
+ const searchInputRef = (0, import_react21.useRef)(null);
1780
+ const searchTimeoutRef = (0, import_react21.useRef)(null);
1781
+ const [imageCache, setImageCache] = (0, import_react21.useState)({});
1782
+ const imageFetchedRef = (0, import_react21.useRef)(/* @__PURE__ */ new Set());
1783
+ const ctx = useOptionalFormRuntimeContext();
1784
+ const searchLookup = searchLookupProp ?? ctx?.searchLookup;
1785
+ const isConnected = isConnectedOverride ?? ctx?.connection?.status === "connected";
1786
+ const targetEntity = control.properties.targetEntity || null;
1787
+ const primaryNameAttr = (0, import_react21.useMemo)(() => {
1788
+ if (!targetEntity) return "name";
1789
+ return getPrimaryNameAttribute(targetEntity);
1790
+ }, [targetEntity]);
1791
+ (0, import_react21.useEffect)(() => {
1792
+ if (value && !selectedRecord) {
1793
+ setSelectedRecord({ id: "", name: value, entityType: targetEntity || void 0 });
1794
+ } else if (!value && selectedRecord) {
1795
+ setSelectedRecord(null);
1796
+ }
1797
+ }, [value]);
1798
+ const performSearch = (0, import_react21.useCallback)(
1799
+ async (query = "") => {
1800
+ if (!searchLookup || !targetEntity) {
1801
+ setRecords([]);
1802
+ return;
1803
+ }
1804
+ setLoading(true);
1805
+ setError(null);
1806
+ try {
1807
+ const entitySetName = resolveEntitySetName(targetEntity);
1808
+ const secondaryField = getSecondaryTextField(targetEntity);
1809
+ const filter = query.trim() ? `contains(${primaryNameAttr},'${query.replace(/'/g, "''")}')` : void 0;
1810
+ const results = await searchLookup(entitySetName, query, { filter, top: 15 });
1811
+ const mappedRecords = results.map((r) => {
1812
+ const secondaryText = secondaryField ? r.record[secondaryField] : void 0;
1813
+ const isDisabled2 = targetEntity === "systemuser" ? r.record["isdisabled"] ?? false : void 0;
1814
+ return {
1815
+ id: r.id,
1816
+ name: r.primaryName || "Unknown",
1817
+ entityType: targetEntity,
1818
+ secondaryText: secondaryText || void 0,
1819
+ isDisabled: isDisabled2
1820
+ };
1821
+ });
1822
+ setRecords(mappedRecords);
1823
+ } catch (err) {
1824
+ setError(err instanceof Error ? err.message : "Search failed");
1825
+ setRecords([]);
1826
+ } finally {
1827
+ setLoading(false);
1828
+ }
1829
+ },
1830
+ [searchLookup, targetEntity, primaryNameAttr, resolveEntitySetName]
1831
+ );
1832
+ const handleSearchInput = (0, import_react21.useCallback)(
1833
+ (newValue) => {
1834
+ setSearchText(newValue);
1835
+ if (searchTimeoutRef.current) {
1836
+ clearTimeout(searchTimeoutRef.current);
1837
+ }
1838
+ searchTimeoutRef.current = setTimeout(() => {
1839
+ performSearch(newValue);
1840
+ }, 300);
1841
+ },
1842
+ [performSearch]
1843
+ );
1844
+ const fetchAndCacheImage = (0, import_react21.useCallback)(
1845
+ async (recordId) => {
1846
+ if (!recordId || !fetchEntityImage) return null;
1847
+ if (imageFetchedRef.current.has(recordId)) return imageCache[recordId] ?? null;
1848
+ imageFetchedRef.current.add(recordId);
1849
+ try {
1850
+ const dataUrl = await fetchEntityImage(recordId);
1851
+ setImageCache((prev) => ({ ...prev, [recordId]: dataUrl }));
1852
+ return dataUrl;
1853
+ } catch {
1854
+ setImageCache((prev) => ({ ...prev, [recordId]: null }));
1855
+ return null;
1856
+ }
1857
+ },
1858
+ [fetchEntityImage, imageCache]
1859
+ );
1860
+ (0, import_react21.useEffect)(() => {
1861
+ if (!isPersonEntity(targetEntity) || records.length === 0 || !fetchEntityImage) return;
1862
+ records.forEach((record) => {
1863
+ if (record.id && !imageFetchedRef.current.has(record.id)) {
1864
+ fetchAndCacheImage(record.id);
1865
+ }
1866
+ });
1867
+ }, [records, targetEntity, fetchEntityImage, fetchAndCacheImage]);
1868
+ const handleOpenDropdown = (0, import_react21.useCallback)(() => {
1869
+ if (disabled || !isConnected || !targetEntity || !searchLookup) return;
1870
+ setIsOpen(true);
1871
+ setSearchText("");
1872
+ performSearch("");
1873
+ setTimeout(() => {
1874
+ searchInputRef.current?.focus();
1875
+ }, 100);
1876
+ }, [disabled, isConnected, targetEntity, searchLookup, performSearch]);
1877
+ const handleRecordSelect = (0, import_react21.useCallback)(
1878
+ (record) => {
1879
+ const cachedImage = record.id ? imageCache[record.id] : void 0;
1880
+ const recordWithImage = cachedImage ? { ...record, imageUrl: cachedImage } : record;
1881
+ setSelectedRecord(recordWithImage);
1882
+ setIsOpen(false);
1883
+ setSearchText("");
1884
+ if (onChange) {
1885
+ onChange(record.name);
1886
+ }
1887
+ if (record.id && !cachedImage && isPersonEntity(targetEntity)) {
1888
+ fetchAndCacheImage(record.id).then((dataUrl) => {
1889
+ if (dataUrl) {
1890
+ setSelectedRecord(
1891
+ (prev) => prev && prev.id === record.id ? { ...prev, imageUrl: dataUrl } : prev
1892
+ );
1893
+ }
1894
+ });
1895
+ }
1896
+ },
1897
+ [onChange, imageCache, targetEntity, fetchAndCacheImage]
1898
+ );
1899
+ const handleClear = (0, import_react21.useCallback)(
1900
+ (e) => {
1901
+ e.stopPropagation();
1902
+ setSelectedRecord(null);
1903
+ setSearchText("");
1904
+ setRecords([]);
1905
+ if (onChange) {
1906
+ onChange("");
1907
+ }
1908
+ },
1909
+ [onChange]
1910
+ );
1911
+ const handleDismiss = (0, import_react21.useCallback)(() => {
1912
+ setIsOpen(false);
1913
+ setSearchText("");
1914
+ }, []);
1915
+ (0, import_react21.useEffect)(() => {
1916
+ return () => {
1917
+ if (searchTimeoutRef.current) {
1918
+ clearTimeout(searchTimeoutRef.current);
1919
+ }
1920
+ };
1921
+ }, []);
1922
+ (0, import_react21.useEffect)(() => {
1923
+ setImageCache({});
1924
+ imageFetchedRef.current.clear();
1925
+ }, [targetEntity]);
1926
+ if (!isConnected || !searchLookup) {
1927
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: `${containerClass2} ${disabled ? containerDisabledClass : ""}`, children: [
1928
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: inputWrapperClass, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1929
+ "input",
1930
+ {
1931
+ type: "text",
1932
+ className: inputClass,
1933
+ placeholder: targetEntity ? `Search ${targetEntity}...` : "Search...",
1934
+ disabled,
1935
+ value: value || "",
1936
+ onChange: (e) => onChange?.(e.target.value)
1937
+ }
1938
+ ) }),
1939
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: searchButtonClass, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react22.Icon, { iconName: "Search", styles: { root: { fontSize: 16, color: "#605e5c" } } }) })
1940
+ ] });
1941
+ }
1942
+ if (!targetEntity) {
1943
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: `${containerClass2} ${containerDisabledClass}`, children: [
1944
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: inputWrapperClass, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: emptyValueClass, children: "Configure target table..." }) }),
1945
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: searchButtonClass, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react22.Icon, { iconName: "Search", styles: { root: { fontSize: 16, color: "#c8c6c4" } } }) })
1946
+ ] });
1947
+ }
1948
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
1949
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
1950
+ "div",
1951
+ {
1952
+ ref: containerRef,
1953
+ className: `${containerClass2} ${disabled ? containerDisabledClass : ""}`,
1954
+ children: [
1955
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: inputWrapperClass, children: selectedRecord ? (
1956
+ // Selected value display with Persona
1957
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: selectedValueClass, children: [
1958
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1959
+ import_react22.Persona,
1960
+ {
1961
+ text: selectedRecord.name,
1962
+ secondaryText: selectedRecord.secondaryText,
1963
+ imageUrl: selectedRecord.imageUrl || void 0,
1964
+ size: import_react22.PersonaSize.size24,
1965
+ presence: isPersonEntity(targetEntity) ? derivePresence(selectedRecord) : import_react22.PersonaPresence.none,
1966
+ hidePersonaDetails: true,
1967
+ styles: {
1968
+ root: { flexShrink: 0 }
1969
+ }
1970
+ }
1971
+ ),
1972
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1973
+ import_react22.Link,
1974
+ {
1975
+ className: selectedLinkClass,
1976
+ onClick: (e) => {
1977
+ e.preventDefault();
1978
+ },
1979
+ title: selectedRecord.name,
1980
+ children: selectedRecord.name
1981
+ }
1982
+ ),
1983
+ !disabled && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1984
+ "div",
1985
+ {
1986
+ className: clearButtonClass,
1987
+ onClick: handleClear,
1988
+ role: "button",
1989
+ tabIndex: 0,
1990
+ title: "Clear selection",
1991
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react22.Icon, { iconName: "Cancel", styles: { root: { fontSize: 12, color: "#605e5c" } } })
1992
+ }
1993
+ )
1994
+ ] })
1995
+ ) : (
1996
+ // Empty state
1997
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: emptyValueClass, children: "---" })
1998
+ ) }),
1999
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2000
+ "div",
2001
+ {
2002
+ className: searchButtonClass,
2003
+ onClick: handleOpenDropdown,
2004
+ role: "button",
2005
+ tabIndex: disabled ? -1 : 0,
2006
+ title: "Look for records",
2007
+ style: { cursor: disabled ? "default" : "pointer" },
2008
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2009
+ import_react22.Icon,
2010
+ {
2011
+ iconName: "Search",
2012
+ styles: { root: { fontSize: 16, color: disabled ? "#c8c6c4" : "#605e5c" } }
2013
+ }
2014
+ )
2015
+ }
2016
+ )
2017
+ ]
2018
+ }
2019
+ ),
2020
+ isOpen && containerRef.current && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2021
+ import_react22.Callout,
2022
+ {
2023
+ target: containerRef.current,
2024
+ isBeakVisible: false,
2025
+ directionalHint: import_react22.DirectionalHint.bottomLeftEdge,
2026
+ onDismiss: handleDismiss,
2027
+ gapSpace: 2,
2028
+ calloutWidth: Math.max(containerRef.current.offsetWidth, 300),
2029
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2030
+ "div",
2031
+ {
2032
+ className: calloutClass,
2033
+ style: { width: Math.max(containerRef.current.offsetWidth, 300) },
2034
+ children: [
2035
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: searchHeaderClass, children: [
2036
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2037
+ "input",
2038
+ {
2039
+ ref: searchInputRef,
2040
+ type: "text",
2041
+ className: searchInputClass,
2042
+ placeholder: "Look for records",
2043
+ value: searchText,
2044
+ onChange: (e) => handleSearchInput(e.target.value)
2045
+ }
2046
+ ),
2047
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react22.Icon, { iconName: "Search", styles: { root: { fontSize: 16, color: "#605e5c" } } })
2048
+ ] }),
2049
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: resultsFromClass, children: [
2050
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: "Results from:" }),
2051
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("button", { className: `${filterTabClass} ${filterTabActiveClass}`, children: [
2052
+ targetEntity.charAt(0).toUpperCase() + targetEntity.slice(1),
2053
+ "s"
2054
+ ] }),
2055
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { className: filterTabClass, children: "Recent records" })
2056
+ ] }),
2057
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: listContainerClass, children: [
2058
+ loading && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: loadingClass, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react22.Spinner, { size: import_react22.SpinnerSize.small, label: "Searching..." }) }),
2059
+ !loading && error && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: noResultsClass, style: { color: "#a80000" }, children: [
2060
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react22.Icon, { iconName: "Warning", styles: { root: { marginRight: 4 } } }),
2061
+ error
2062
+ ] }),
2063
+ !loading && !error && records.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: noResultsClass, children: "No records found" }),
2064
+ !loading && !error && records.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react22.FocusZone, { direction: import_react22.FocusZoneDirection.vertical, children: records.map((record) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2065
+ "button",
2066
+ {
2067
+ className: recordItemClass,
2068
+ onClick: () => handleRecordSelect(record),
2069
+ children: [
2070
+ isPersonEntity(targetEntity) ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2071
+ import_react22.Persona,
2072
+ {
2073
+ text: record.name,
2074
+ secondaryText: record.secondaryText,
2075
+ imageUrl: record.id ? imageCache[record.id] || void 0 : void 0,
2076
+ size: import_react22.PersonaSize.size32,
2077
+ presence: derivePresence(record),
2078
+ hidePersonaDetails: true,
2079
+ styles: { root: { flexShrink: 0 } }
2080
+ }
2081
+ ) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: entityIconClass, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react22.Icon, { iconName: getEntityIcon(targetEntity) }) }),
2082
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: [
2083
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: recordNameClass, children: record.name }),
2084
+ record.secondaryText && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2085
+ "div",
2086
+ {
2087
+ style: {
2088
+ fontSize: 12,
2089
+ color: "#605e5c",
2090
+ overflow: "hidden",
2091
+ textOverflow: "ellipsis",
2092
+ whiteSpace: "nowrap"
2093
+ },
2094
+ children: record.secondaryText
2095
+ }
2096
+ )
2097
+ ] })
2098
+ ]
2099
+ },
2100
+ record.id || record.name
2101
+ )) })
2102
+ ] }),
2103
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: footerClass, children: [
2104
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2105
+ "button",
2106
+ {
2107
+ className: footerButtonClass,
2108
+ onClick: () => {
2109
+ },
2110
+ children: [
2111
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react22.Icon, { iconName: "Add", styles: { root: { fontSize: 14 } } }),
2112
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: "New" })
2113
+ ]
2114
+ }
2115
+ ),
2116
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2117
+ "button",
2118
+ {
2119
+ className: footerButtonClass,
2120
+ onClick: () => {
2121
+ },
2122
+ children: [
2123
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react22.Icon, { iconName: "Search", styles: { root: { fontSize: 14 } } }),
2124
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: "Advanced" })
2125
+ ]
2126
+ }
2127
+ )
2128
+ ] })
2129
+ ]
2130
+ }
2131
+ )
2132
+ }
2133
+ )
2134
+ ] });
2135
+ };
2136
+ var lookupEntityHelpers = {
2137
+ getPrimaryNameAttribute,
2138
+ getSecondaryTextField,
2139
+ isPersonEntity,
2140
+ getEntityIcon,
2141
+ getInitials
2142
+ };
2143
+
2144
+ // src/v8/controls/lookup.tsx
2145
+ var import_jsx_runtime22 = require("react/jsx-runtime");
2146
+ var LookupBranch = ({
2147
+ control,
2148
+ displayValue,
2149
+ hasValue,
2150
+ interactive,
2151
+ onChange
2152
+ }) => {
2153
+ const disabled = isDisabled(control, interactive);
2154
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2155
+ LookupControl,
2156
+ {
2157
+ control,
2158
+ disabled,
2159
+ value: hasValue ? displayValue : void 0,
2160
+ onChange: onChange ? (next) => onChange(next) : void 0
2161
+ }
2162
+ );
2163
+ };
2164
+
2165
+ // src/v8/controls/internals/WebResourceRenderer.tsx
2166
+ var import_react23 = require("react");
2167
+ var import_react24 = require("@fluentui/react");
2168
+ var import_jsx_runtime23 = require("react/jsx-runtime");
2169
+ var TYPE_CONFIG = {
2170
+ HTML: { icon: "FileHTML", color: "#e44d26", label: "HTML Web Page" },
2171
+ CSS: { icon: "FileCSS", color: "#264de4", label: "Style Sheet (CSS)" },
2172
+ JS: { icon: "JavaScriptLanguage", color: "#f7df1e", label: "Script (JScript)" },
2173
+ PNG: { icon: "FileImage", color: "#0078d4", label: "PNG Image" },
2174
+ JPG: { icon: "FileImage", color: "#0078d4", label: "JPG Image" },
2175
+ GIF: { icon: "FileImage", color: "#0078d4", label: "GIF Image" },
2176
+ SVG: { icon: "FileImage", color: "#ffb13b", label: "Vector (SVG)" },
2177
+ ICO: { icon: "FileImage", color: "#0078d4", label: "ICO Image" },
2178
+ XML: { icon: "FileCode", color: "#f26522", label: "Data (XML)" },
2179
+ XSL: { icon: "FileCode", color: "#68217a", label: "Style Sheet (XSL)" },
2180
+ RESX: { icon: "LocaleLanguage", color: "#107c10", label: "String (RESX)" }
2181
+ };
2182
+ var IMAGE_TYPES = /* @__PURE__ */ new Set(["PNG", "JPG", "GIF", "SVG", "ICO"]);
2183
+ var CODE_TYPES = /* @__PURE__ */ new Set(["CSS", "JS", "XML", "XSL", "RESX"]);
2184
+ var WebResourceRenderer = ({ control }) => {
2185
+ const webResourceName = control.properties.webResourceName || "";
2186
+ const webResourceType = control.properties.webResourceType || "HTML";
2187
+ const height = control.properties.height ?? 200;
2188
+ const passRecordId = control.properties.passRecordId ?? false;
2189
+ const ctx = useOptionalFormRuntimeContext();
2190
+ const isConnected = ctx?.connection.status === "connected";
2191
+ const environmentUrl = ctx?.connection.dynamicsUrl ?? null;
2192
+ const previewRecordId = ctx?.previewContext.recordId ?? null;
2193
+ const isHtml = webResourceType === "HTML";
2194
+ const isImage = IMAGE_TYPES.has(webResourceType);
2195
+ const isCode = CODE_TYPES.has(webResourceType);
2196
+ const [fetchState, setFetchState] = (0, import_react23.useState)({
2197
+ content: null,
2198
+ loading: false,
2199
+ error: null
2200
+ });
2201
+ const typeConfig = TYPE_CONFIG[webResourceType] ?? TYPE_CONFIG.HTML;
2202
+ (0, import_react23.useEffect)(() => {
2203
+ if (!ctx?.fetchWebResource || !webResourceName || isHtml || !isConnected) {
2204
+ setFetchState({ content: null, loading: false, error: null });
2205
+ return;
2206
+ }
2207
+ let cancelled = false;
2208
+ const controller = new AbortController();
2209
+ setFetchState({ content: null, loading: true, error: null });
2210
+ ctx.fetchWebResource(webResourceName, void 0, { signal: controller.signal }).then((data) => {
2211
+ if (cancelled) return;
2212
+ const { content, contentType } = data;
2213
+ const rendered = contentType.startsWith("image/") ? `data:${contentType};base64,${content}` : content;
2214
+ setFetchState({ content: rendered, loading: false, error: null });
2215
+ }).catch((err) => {
2216
+ if (cancelled) return;
2217
+ setFetchState({
2218
+ content: null,
2219
+ loading: false,
2220
+ error: err instanceof Error ? err.message : "Failed to fetch web resource"
2221
+ });
2222
+ });
2223
+ return () => {
2224
+ cancelled = true;
2225
+ controller.abort();
2226
+ };
2227
+ }, [ctx, webResourceName, isHtml, isConnected]);
2228
+ const webResourceUrl = (0, import_react23.useMemo)(() => {
2229
+ if (!isHtml || !environmentUrl || !webResourceName) return null;
2230
+ let url = `${environmentUrl}/WebResources/${webResourceName}`;
2231
+ if (passRecordId && previewRecordId) {
2232
+ url += `?id=${previewRecordId}`;
2233
+ }
2234
+ return url;
2235
+ }, [isHtml, environmentUrl, webResourceName, passRecordId, previewRecordId]);
2236
+ const hasImageContent = isImage && fetchState.content && !fetchState.loading && !fetchState.error;
2237
+ const hasCodeContent = isCode && fetchState.content && !fetchState.loading && !fetchState.error;
2238
+ const hasUrlContent = isHtml && isConnected && webResourceUrl;
2239
+ const hasLiveContent = hasUrlContent || hasImageContent || hasCodeContent;
2240
+ const renderLiveContent = () => {
2241
+ if (isHtml) {
2242
+ if (!isConnected) {
2243
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
2244
+ "div",
2245
+ {
2246
+ style: {
2247
+ flex: 1,
2248
+ display: "flex",
2249
+ flexDirection: "column",
2250
+ alignItems: "center",
2251
+ justifyContent: "center",
2252
+ backgroundColor: "#deecf9",
2253
+ gap: 8,
2254
+ padding: 16
2255
+ },
2256
+ children: [
2257
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react24.Icon, { iconName: "PlugDisconnected", styles: { root: { fontSize: 32, color: "#0078d4" } } }),
2258
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { fontSize: 12, color: "#0078d4", textAlign: "center" }, children: "Connect to Dataverse to preview web resource" })
2259
+ ]
2260
+ }
2261
+ );
2262
+ }
2263
+ if (!webResourceName) {
2264
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
2265
+ "div",
2266
+ {
2267
+ style: {
2268
+ flex: 1,
2269
+ display: "flex",
2270
+ flexDirection: "column",
2271
+ alignItems: "center",
2272
+ justifyContent: "center",
2273
+ backgroundColor: "#faf9f8",
2274
+ gap: 8
2275
+ },
2276
+ children: [
2277
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2278
+ "div",
2279
+ {
2280
+ style: {
2281
+ width: 48,
2282
+ height: 48,
2283
+ borderRadius: 8,
2284
+ backgroundColor: typeConfig.color,
2285
+ display: "flex",
2286
+ alignItems: "center",
2287
+ justifyContent: "center"
2288
+ },
2289
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react24.Icon, { iconName: typeConfig.icon, styles: { root: { fontSize: 24, color: "#fff" } } })
2290
+ }
2291
+ ),
2292
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { fontSize: 13, color: "#605e5c" }, children: "No web resource selected" }),
2293
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { fontSize: 11, color: "#a19f9d" }, children: "Configure in Properties panel" })
2294
+ ]
2295
+ }
2296
+ );
2297
+ }
2298
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2299
+ "iframe",
2300
+ {
2301
+ src: webResourceUrl,
2302
+ title: webResourceName,
2303
+ sandbox: "allow-scripts allow-same-origin allow-forms",
2304
+ style: {
2305
+ flex: 1,
2306
+ width: "100%",
2307
+ border: "none",
2308
+ backgroundColor: "#fff"
2309
+ }
2310
+ }
2311
+ );
2312
+ }
2313
+ if (isImage && hasImageContent) {
2314
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2315
+ "div",
2316
+ {
2317
+ style: {
2318
+ flex: 1,
2319
+ display: "flex",
2320
+ alignItems: "center",
2321
+ justifyContent: "center",
2322
+ padding: 8,
2323
+ overflow: "hidden"
2324
+ },
2325
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2326
+ "img",
2327
+ {
2328
+ src: fetchState.content,
2329
+ alt: webResourceName,
2330
+ style: {
2331
+ maxWidth: "100%",
2332
+ maxHeight: "100%",
2333
+ objectFit: "contain"
2334
+ }
2335
+ }
2336
+ )
2337
+ }
2338
+ );
2339
+ }
2340
+ if (isCode && hasCodeContent) {
2341
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2342
+ "div",
2343
+ {
2344
+ style: {
2345
+ flex: 1,
2346
+ overflow: "auto",
2347
+ padding: 8,
2348
+ backgroundColor: "#1e1e1e"
2349
+ },
2350
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
2351
+ "pre",
2352
+ {
2353
+ style: {
2354
+ margin: 0,
2355
+ fontSize: 11,
2356
+ fontFamily: "Consolas, monospace",
2357
+ color: "#d4d4d4",
2358
+ whiteSpace: "pre-wrap",
2359
+ wordBreak: "break-all"
2360
+ },
2361
+ children: [
2362
+ fetchState.content.substring(0, 2e3),
2363
+ fetchState.content.length > 2e3 ? "\n\n... (truncated)" : ""
2364
+ ]
2365
+ }
2366
+ )
2367
+ }
2368
+ );
2369
+ }
2370
+ return null;
2371
+ };
2372
+ const renderPlaceholder = () => {
2373
+ if (fetchState.loading) {
2374
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2375
+ "div",
2376
+ {
2377
+ style: {
2378
+ flex: 1,
2379
+ display: "flex",
2380
+ flexDirection: "column",
2381
+ alignItems: "center",
2382
+ justifyContent: "center",
2383
+ gap: 8
2384
+ },
2385
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react24.Spinner, { size: import_react24.SpinnerSize.medium, label: "Loading web resource..." })
2386
+ }
2387
+ );
2388
+ }
2389
+ if (fetchState.error) {
2390
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
2391
+ "div",
2392
+ {
2393
+ style: {
2394
+ flex: 1,
2395
+ display: "flex",
2396
+ flexDirection: "column",
2397
+ alignItems: "center",
2398
+ justifyContent: "center",
2399
+ backgroundColor: "#fef0f0",
2400
+ gap: 8,
2401
+ padding: 16
2402
+ },
2403
+ children: [
2404
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react24.Icon, { iconName: "Warning", styles: { root: { fontSize: 32, color: "#d13438" } } }),
2405
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { fontSize: 12, color: "#a80000", textAlign: "center" }, children: fetchState.error })
2406
+ ]
2407
+ }
2408
+ );
2409
+ }
2410
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
2411
+ "div",
2412
+ {
2413
+ style: {
2414
+ flex: 1,
2415
+ display: "flex",
2416
+ flexDirection: "column",
2417
+ alignItems: "center",
2418
+ justifyContent: "center",
2419
+ backgroundColor: "#faf9f8",
2420
+ gap: 8
2421
+ },
2422
+ children: [
2423
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2424
+ "div",
2425
+ {
2426
+ style: {
2427
+ width: 48,
2428
+ height: 48,
2429
+ borderRadius: 8,
2430
+ backgroundColor: typeConfig.color,
2431
+ display: "flex",
2432
+ alignItems: "center",
2433
+ justifyContent: "center"
2434
+ },
2435
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react24.Icon, { iconName: typeConfig.icon, styles: { root: { fontSize: 24, color: "#fff" } } })
2436
+ }
2437
+ ),
2438
+ webResourceName ? /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
2439
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { fontSize: 13, fontWeight: 600, color: "#323130" }, children: webResourceName }),
2440
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { fontSize: 11, color: "#605e5c" }, children: typeConfig.label }),
2441
+ passRecordId && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
2442
+ "div",
2443
+ {
2444
+ style: {
2445
+ fontSize: 10,
2446
+ color: "#605e5c",
2447
+ backgroundColor: "#f3f2f1",
2448
+ padding: "4px 8px",
2449
+ borderRadius: 4,
2450
+ fontFamily: "monospace"
2451
+ },
2452
+ children: [
2453
+ "?id=",
2454
+ "{recordid}"
2455
+ ]
2456
+ }
2457
+ ),
2458
+ !isConnected && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2459
+ "div",
2460
+ {
2461
+ style: {
2462
+ fontSize: 10,
2463
+ color: "#0078d4",
2464
+ backgroundColor: "#deecf9",
2465
+ padding: "4px 8px",
2466
+ borderRadius: 4,
2467
+ marginTop: 4
2468
+ },
2469
+ children: "Connect to Dataverse for live preview"
2470
+ }
2471
+ )
2472
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
2473
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { fontSize: 13, color: "#605e5c" }, children: "No web resource selected" }),
2474
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { fontSize: 11, color: "#a19f9d" }, children: "Configure in Properties panel" })
2475
+ ] })
2476
+ ]
2477
+ }
2478
+ );
2479
+ };
2480
+ const renderContent = () => {
2481
+ if (isHtml) return renderLiveContent();
2482
+ if (hasLiveContent) return renderLiveContent();
2483
+ return renderPlaceholder();
2484
+ };
2485
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
2486
+ "div",
2487
+ {
2488
+ style: {
2489
+ height,
2490
+ border: "1px solid #edebe9",
2491
+ borderRadius: 2,
2492
+ overflow: "hidden",
2493
+ backgroundColor: "#fff",
2494
+ display: "flex",
2495
+ flexDirection: "column"
2496
+ },
2497
+ children: [
2498
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
2499
+ "div",
2500
+ {
2501
+ style: {
2502
+ display: "flex",
2503
+ alignItems: "center",
2504
+ padding: "6px 12px",
2505
+ borderBottom: "1px solid #edebe9",
2506
+ backgroundColor: "#faf9f8",
2507
+ gap: 8,
2508
+ flexShrink: 0
2509
+ },
2510
+ children: [
2511
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react24.Icon, { iconName: "Globe", styles: { root: { fontSize: 14, color: "#0078d4" } } }),
2512
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { style: { fontSize: 12, fontWeight: 600, color: "#323130" }, children: "Web Resource" }),
2513
+ isHtml && isConnected && webResourceName && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2514
+ "span",
2515
+ {
2516
+ style: {
2517
+ fontSize: 10,
2518
+ color: "#0078d4",
2519
+ backgroundColor: "#deecf9",
2520
+ padding: "2px 6px",
2521
+ borderRadius: 2
2522
+ },
2523
+ children: "URL"
2524
+ }
2525
+ ),
2526
+ !isHtml && hasLiveContent && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2527
+ "span",
2528
+ {
2529
+ style: {
2530
+ fontSize: 10,
2531
+ color: "#107c10",
2532
+ backgroundColor: "#dff6dd",
2533
+ padding: "2px 6px",
2534
+ borderRadius: 2
2535
+ },
2536
+ children: "Live"
2537
+ }
2538
+ ),
2539
+ passRecordId && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2540
+ "span",
2541
+ {
2542
+ style: {
2543
+ fontSize: 10,
2544
+ color: "#107c10",
2545
+ backgroundColor: "#dff6dd",
2546
+ padding: "2px 6px",
2547
+ borderRadius: 2
2548
+ },
2549
+ children: "+ Record ID"
2550
+ }
2551
+ ),
2552
+ webResourceName && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { style: { fontSize: 11, color: "#605e5c", marginLeft: "auto" }, children: typeConfig.label })
2553
+ ]
2554
+ }
2555
+ ),
2556
+ renderContent(),
2557
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
2558
+ "div",
2559
+ {
2560
+ style: {
2561
+ display: "flex",
2562
+ alignItems: "center",
2563
+ justifyContent: "space-between",
2564
+ padding: "4px 12px",
2565
+ borderTop: "1px solid #edebe9",
2566
+ backgroundColor: "#faf9f8",
2567
+ fontSize: 10,
2568
+ color: "#a19f9d",
2569
+ flexShrink: 0
2570
+ },
2571
+ children: [
2572
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { children: webResourceName || "Not configured" }),
2573
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { children: [
2574
+ "Height: ",
2575
+ height,
2576
+ "px"
2577
+ ] })
2578
+ ]
2579
+ }
2580
+ )
2581
+ ]
2582
+ }
2583
+ );
2584
+ };
2585
+
2586
+ // src/v8/controls/webresource.tsx
2587
+ var import_jsx_runtime24 = require("react/jsx-runtime");
2588
+ var WebResourceBranch = ({ control, interactive }) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(WebResourceRenderer, { control, designMode: !interactive });
2589
+
2590
+ // src/v8/controls/internals/ChartRenderer.tsx
2591
+ var import_react25 = require("@fluentui/react");
2592
+ var import_jsx_runtime25 = require("react/jsx-runtime");
2593
+ var cardClass = (0, import_react25.mergeStyles)({
2594
+ display: "flex",
2595
+ flexDirection: "column",
2596
+ height: "100%",
2597
+ minHeight: 140,
2598
+ padding: 12,
2599
+ borderRadius: 4,
2600
+ border: "1px dashed #c8c6c4",
2601
+ backgroundColor: "#faf9f8",
2602
+ color: "#323130",
2603
+ gap: 8
2604
+ });
2605
+ var headerClass = (0, import_react25.mergeStyles)({
2606
+ display: "flex",
2607
+ alignItems: "center",
2608
+ gap: 8,
2609
+ marginBottom: 4
2610
+ });
2611
+ var iconClass = (0, import_react25.mergeStyles)({
2612
+ fontSize: 20,
2613
+ color: "#0078d4"
2614
+ });
2615
+ var titleClass = (0, import_react25.mergeStyles)({
2616
+ fontWeight: 600,
2617
+ fontSize: 14,
2618
+ flex: 1,
2619
+ overflow: "hidden",
2620
+ textOverflow: "ellipsis",
2621
+ whiteSpace: "nowrap"
2622
+ });
2623
+ var v9BadgeClass = (0, import_react25.mergeStyles)({
2624
+ fontSize: 10,
2625
+ fontWeight: 600,
2626
+ padding: "2px 6px",
2627
+ borderRadius: 10,
2628
+ backgroundColor: "#fff4ce",
2629
+ color: "#7a4d00",
2630
+ border: "1px solid #ffb900"
2631
+ });
2632
+ var bodyClass = (0, import_react25.mergeStyles)({
2633
+ flex: 1,
2634
+ display: "flex",
2635
+ alignItems: "center",
2636
+ justifyContent: "center",
2637
+ minHeight: 60
2638
+ });
2639
+ var metaClass = (0, import_react25.mergeStyles)({
2640
+ fontSize: 11,
2641
+ color: "#605e5c",
2642
+ display: "flex",
2643
+ flexDirection: "column",
2644
+ gap: 2
2645
+ });
2646
+ function formatBindingSummary(config, sourceLabel) {
2647
+ if (!config?.dataSourceId) return "Sample data \u2014 bind to a data source";
2648
+ const parts = [];
2649
+ if (sourceLabel) parts.push(sourceLabel);
2650
+ if (config.aggregation && config.valueAttribute) {
2651
+ parts.push(`${config.aggregation}(${config.valueAttribute})`);
2652
+ } else if (config.valueAttribute) {
2653
+ parts.push(config.valueAttribute);
2654
+ }
2655
+ if (config.categoryAttribute) parts.push(`by ${config.categoryAttribute}`);
2656
+ return parts.length > 0 ? parts.join(" \xB7 ") : "Data source bound";
2657
+ }
2658
+ var ChartRenderer = ({
2659
+ control,
2660
+ dataSourceLabelById,
2661
+ entry
2662
+ }) => {
2663
+ const chartConfig = control.properties.chartConfig;
2664
+ const title = chartConfig?.title ?? control.label;
2665
+ const sourceLabel = chartConfig?.dataSourceId ? dataSourceLabelById?.[chartConfig.dataSourceId] ?? chartConfig.dataSourceId : null;
2666
+ const requiresV9 = entry?.requiresV9 === true;
2667
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: cardClass, "aria-label": `${entry?.label ?? "Chart"}: ${title}`, children: [
2668
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: headerClass, children: [
2669
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react25.Icon, { iconName: entry?.icon ?? "BarChartVertical", className: iconClass }),
2670
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: titleClass, children: title }),
2671
+ requiresV9 && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: v9BadgeClass, title: "This chart type renders only in v9 export targets", children: "v9" })
2672
+ ] }),
2673
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: bodyClass, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_react25.Text, { variant: "small", styles: { root: { color: "#605e5c", fontStyle: "italic" } }, children: [
2674
+ entry?.label ?? "Chart",
2675
+ " preview"
2676
+ ] }) }),
2677
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: metaClass, children: [
2678
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: formatBindingSummary(chartConfig, sourceLabel) }),
2679
+ requiresV9 && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { style: { color: "#7a4d00" }, children: "Requires Fluent UI v9 export \u2014 Power Pages / Canvas targets are blocked." })
2680
+ ] })
2681
+ ] });
2682
+ };
2683
+ var CHART_CONTROL_TYPES = /* @__PURE__ */ new Set([
2684
+ "barchart",
2685
+ "linechart",
2686
+ "areachart",
2687
+ "piechart",
2688
+ "donutchart",
2689
+ "horizontalbarchart",
2690
+ "gaugechart",
2691
+ "sparkline",
2692
+ "heatmapchart",
2693
+ "kpicard",
2694
+ "funnelchart",
2695
+ "scatterchart"
2696
+ ]);
2697
+ function isChartControlType(type) {
2698
+ return CHART_CONTROL_TYPES.has(type);
2699
+ }
2700
+
2701
+ // src/v8/controls/chart.tsx
2702
+ var import_jsx_runtime26 = require("react/jsx-runtime");
2703
+ var ChartBranch = ({ control, dataSourceLabelById, entry }) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ChartRenderer, { control, dataSourceLabelById, entry });
2704
+
2705
+ // src/v8/controls/persona.tsx
2706
+ var import_react26 = require("@fluentui/react");
2707
+
2708
+ // src/v8/shared/helpers.ts
2709
+ var PERSONA_COLORS = [
2710
+ "#0078d4",
2711
+ "#00bcf2",
2712
+ "#008272",
2713
+ "#107c10",
2714
+ "#5c2d91",
2715
+ "#b4009e",
2716
+ "#e3008c",
2717
+ "#d83b01",
2718
+ "#ca5010",
2719
+ "#986f0b",
2720
+ "#498205",
2721
+ "#038387"
2722
+ ];
2723
+ function getInitials2(name) {
2724
+ if (!name) return "?";
2725
+ const words = name.trim().split(/\s+/);
2726
+ if (words.length >= 2) {
2727
+ return (words[0][0] + words[1][0]).toUpperCase();
2728
+ }
2729
+ return words[0].substring(0, 2).toUpperCase();
2730
+ }
2731
+ function getPersonaColor(name) {
2732
+ let hash = 0;
2733
+ for (let i = 0; i < name.length; i++) {
2734
+ hash = (hash << 5) - hash + name.charCodeAt(i) | 0;
2735
+ }
2736
+ return PERSONA_COLORS[Math.abs(hash) % PERSONA_COLORS.length];
2737
+ }
2738
+ function isEmbeddedEntityBrowser(webResourceName) {
2739
+ if (!webResourceName) return false;
2740
+ const normalized = webResourceName.toLowerCase().replace(/[_/]/g, "");
2741
+ return normalized.includes("entitybrowser");
2742
+ }
2743
+
2744
+ // src/v8/controls/persona.tsx
2745
+ var import_jsx_runtime27 = require("react/jsx-runtime");
2746
+ var SIZE_MAP = {
2747
+ size24: import_react26.PersonaSize.size24,
2748
+ size32: import_react26.PersonaSize.size32,
2749
+ size40: import_react26.PersonaSize.size40,
2750
+ size48: import_react26.PersonaSize.size48,
2751
+ size56: import_react26.PersonaSize.size56,
2752
+ size72: import_react26.PersonaSize.size72,
2753
+ size100: import_react26.PersonaSize.size100,
2754
+ size120: import_react26.PersonaSize.size120
2755
+ };
2756
+ var PRESENCE_MAP = {
2757
+ none: import_react26.PersonaPresence.none,
2758
+ online: import_react26.PersonaPresence.online,
2759
+ away: import_react26.PersonaPresence.away,
2760
+ busy: import_react26.PersonaPresence.busy,
2761
+ dnd: import_react26.PersonaPresence.dnd,
2762
+ offline: import_react26.PersonaPresence.offline,
2763
+ blocked: import_react26.PersonaPresence.blocked
2764
+ };
2765
+ var PersonaControl = ({ control }) => {
2766
+ const rawSourceMode = control.properties.personaSourceMode || "static";
2767
+ const sourceMode = rawSourceMode === "systemUser" ? "dataverse" : rawSourceMode;
2768
+ const personaSize = control.properties.personaSize || "size48";
2769
+ const showPresence = control.properties.showPresence ?? false;
2770
+ const presenceOverride = control.properties.presence || "none";
2771
+ const hideDetails = control.properties.hideDetails ?? false;
2772
+ const showSecondaryText = control.properties.showSecondaryText ?? true;
2773
+ let displayText;
2774
+ let displaySecondary;
2775
+ let displayImageUrl;
2776
+ let derivedPresence;
2777
+ if (sourceMode === "dataverse") {
2778
+ const entityName = control.properties.dvRecordEntityName || "systemuser";
2779
+ const recordId = control.properties.dvRecordId || control.properties.systemUserId || "";
2780
+ displayText = control.properties.dvRecordName || control.properties.systemUserName || "Select a record...";
2781
+ displaySecondary = control.properties.dvRecordSecondaryText || control.properties.systemUserJobTitle || "";
2782
+ displayImageUrl = control.properties.dvRecordImageDataUrl || control.properties.systemUserImageDataUrl || "";
2783
+ if (presenceOverride !== "none") {
2784
+ derivedPresence = PRESENCE_MAP[presenceOverride] ?? import_react26.PersonaPresence.none;
2785
+ } else if (entityName === "systemuser" && recordId) {
2786
+ const isDisabled2 = control.properties.dvRecordIsDisabled ?? control.properties.systemUserIsDisabled ?? false;
2787
+ derivedPresence = isDisabled2 ? import_react26.PersonaPresence.offline : import_react26.PersonaPresence.online;
2788
+ } else {
2789
+ derivedPresence = import_react26.PersonaPresence.none;
2790
+ }
2791
+ } else {
2792
+ displayText = control.properties.primaryText || "User Name";
2793
+ displaySecondary = control.properties.secondaryText || "";
2794
+ displayImageUrl = control.properties.imageUrl || "";
2795
+ derivedPresence = PRESENCE_MAP[presenceOverride] ?? import_react26.PersonaPresence.none;
2796
+ }
2797
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2798
+ import_react26.Persona,
2799
+ {
2800
+ text: displayText,
2801
+ secondaryText: showSecondaryText ? displaySecondary : void 0,
2802
+ imageUrl: displayImageUrl || void 0,
2803
+ imageInitials: getInitials2(displayText),
2804
+ size: SIZE_MAP[personaSize] ?? import_react26.PersonaSize.size48,
2805
+ initialsColor: getPersonaColor(displayText),
2806
+ presence: showPresence ? derivedPresence : import_react26.PersonaPresence.none,
2807
+ hidePersonaDetails: hideDetails,
2808
+ showSecondaryText: showSecondaryText && !!displaySecondary
2809
+ }
2810
+ );
2811
+ };
2812
+
2813
+ // src/v8/controls/button.tsx
2814
+ var import_react27 = require("react");
2815
+ var import_react28 = require("@fluentui/react");
2816
+ var import_jsx_runtime28 = require("react/jsx-runtime");
2817
+ var ButtonControl = ({
2818
+ control,
2819
+ interactive,
2820
+ onButtonClick
2821
+ }) => {
2822
+ const buttonVariant = control.properties.buttonVariant ?? "default";
2823
+ const isPrimary = control.properties.primary ?? buttonVariant === "primary";
2824
+ const buttonText = control.properties.buttonText ?? control.label ?? "Button";
2825
+ const buttonAction = control.properties.buttonAction ?? { type: "none" };
2826
+ const iconName = control.properties.iconName;
2827
+ const iconPosition = control.properties.iconPosition ?? "before";
2828
+ const buttonSize = control.properties.size ?? "medium";
2829
+ const fullWidth = control.properties.fullWidth ?? false;
2830
+ const alignment = control.properties.alignment ?? "left";
2831
+ const buttonDisabled = control.properties.disabled ?? false;
2832
+ const tooltip = control.properties.tooltip;
2833
+ const secondaryText = control.properties.secondaryText;
2834
+ const iconOnly = control.properties.iconOnly ?? false;
2835
+ const backgroundColor = control.properties.backgroundColor;
2836
+ const textColor = control.properties.textColor;
2837
+ const iconColor = control.properties.iconColor;
2838
+ const menuItems = control.properties.menuItems;
2839
+ const commandBarItems = control.properties.commandBarItems;
2840
+ const commandBarIconOnly = control.properties.commandBarIconOnly ?? false;
2841
+ const commandBarMainPosition = control.properties.commandBarMainPosition ?? 0;
2842
+ const checkedText = control.properties.checkedText;
2843
+ const uncheckedText = control.properties.uncheckedText;
2844
+ const checkedIconName = control.properties.checkedIconName;
2845
+ const checkedBackgroundColor = control.properties.checkedBackgroundColor;
2846
+ const checkedTextColor = control.properties.checkedTextColor;
2847
+ const checkedIconColor = control.properties.checkedIconColor;
2848
+ const designMode = !interactive;
2849
+ const [toggleChecked, setToggleChecked] = (0, import_react27.useState)(false);
2850
+ const sizeHeightMap = { small: 24, medium: 32, large: 40 };
2851
+ const btnHeight = sizeHeightMap[buttonSize] ?? 32;
2852
+ const handleClick = () => {
2853
+ if (designMode || !onButtonClick) return;
2854
+ onButtonClick(buttonAction);
2855
+ };
2856
+ const iconProps = iconName ? { iconName } : void 0;
2857
+ const resolvedIconColor = iconColor || textColor;
2858
+ const displayText = iconOnly && iconName ? void 0 : buttonText;
2859
+ const customStyles = {
2860
+ root: {
2861
+ height: btnHeight,
2862
+ minWidth: buttonSize === "small" ? 60 : void 0,
2863
+ ...fullWidth && { width: "100%" },
2864
+ ...backgroundColor && { backgroundColor, borderColor: backgroundColor },
2865
+ ...textColor && { color: textColor }
2866
+ },
2867
+ flexContainer: {
2868
+ ...iconPosition === "after" && { flexDirection: "row-reverse" }
2869
+ },
2870
+ label: {
2871
+ ...textColor && { color: textColor },
2872
+ ...iconOnly && iconName && { display: "none" }
2873
+ },
2874
+ icon: {
2875
+ ...resolvedIconColor && { color: resolvedIconColor }
2876
+ }
2877
+ };
2878
+ const menuProps = (buttonVariant === "split" || buttonVariant === "command") && menuItems && menuItems.length > 0 ? {
2879
+ items: menuItems.map((mi) => ({
2880
+ key: mi.id,
2881
+ text: mi.text,
2882
+ iconProps: mi.iconName ? { iconName: mi.iconName } : void 0,
2883
+ onClick: () => {
2884
+ if (!designMode && onButtonClick) {
2885
+ onButtonClick(mi.action);
2886
+ }
2887
+ }
2888
+ }))
2889
+ } : void 0;
2890
+ const justifyMap = {
2891
+ left: "flex-start",
2892
+ center: "center",
2893
+ right: "flex-end"
2894
+ };
2895
+ const wrapperStyle = {
2896
+ display: "flex",
2897
+ justifyContent: fullWidth ? "stretch" : justifyMap[alignment] ?? "flex-start",
2898
+ padding: "4px 0",
2899
+ position: "relative"
2900
+ };
2901
+ const renderButton = () => {
2902
+ switch (buttonVariant) {
2903
+ case "primary":
2904
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2905
+ import_react28.DefaultButton,
2906
+ {
2907
+ text: displayText,
2908
+ iconProps,
2909
+ onClick: handleClick,
2910
+ disabled: designMode || buttonDisabled,
2911
+ styles: customStyles,
2912
+ primary: true
2913
+ }
2914
+ );
2915
+ case "compound":
2916
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2917
+ import_react28.CompoundButton,
2918
+ {
2919
+ text: displayText ?? buttonText,
2920
+ secondaryText: secondaryText || "Description",
2921
+ iconProps,
2922
+ onClick: handleClick,
2923
+ disabled: designMode || buttonDisabled,
2924
+ styles: customStyles,
2925
+ primary: isPrimary
2926
+ }
2927
+ );
2928
+ case "icon":
2929
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2930
+ import_react28.IconButton,
2931
+ {
2932
+ iconProps: iconProps || { iconName: "Add" },
2933
+ title: buttonText,
2934
+ ariaLabel: buttonText,
2935
+ onClick: handleClick,
2936
+ disabled: designMode || buttonDisabled,
2937
+ styles: {
2938
+ root: {
2939
+ height: btnHeight,
2940
+ width: btnHeight,
2941
+ ...backgroundColor && { backgroundColor, borderColor: backgroundColor }
2942
+ },
2943
+ icon: {
2944
+ ...resolvedIconColor && { color: resolvedIconColor }
2945
+ }
2946
+ }
2947
+ }
2948
+ );
2949
+ case "action":
2950
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2951
+ import_react28.ActionButton,
2952
+ {
2953
+ text: displayText,
2954
+ iconProps,
2955
+ onClick: handleClick,
2956
+ disabled: designMode || buttonDisabled,
2957
+ styles: customStyles
2958
+ }
2959
+ );
2960
+ case "command": {
2961
+ const cmdButtonStyles = {
2962
+ root: {
2963
+ height: btnHeight,
2964
+ ...backgroundColor && { backgroundColor, borderColor: backgroundColor },
2965
+ ...textColor && { color: textColor }
2966
+ },
2967
+ label: {
2968
+ ...textColor && { color: textColor }
2969
+ },
2970
+ icon: {
2971
+ ...resolvedIconColor && { color: resolvedIconColor }
2972
+ },
2973
+ menuIcon: {
2974
+ ...textColor && { color: textColor }
2975
+ }
2976
+ };
2977
+ const mainButtonItem = {
2978
+ key: "main",
2979
+ text: displayText,
2980
+ iconProps,
2981
+ iconOnly: iconOnly && !!iconName,
2982
+ ariaLabel: buttonText,
2983
+ onClick: handleClick,
2984
+ disabled: designMode || buttonDisabled,
2985
+ buttonStyles: cmdButtonStyles,
2986
+ subMenuProps: menuProps
2987
+ };
2988
+ const additionalItems = [];
2989
+ if (commandBarItems && commandBarItems.length > 0) {
2990
+ commandBarItems.forEach((cbi) => {
2991
+ const cbiSubMenuProps = cbi.subMenuItems && cbi.subMenuItems.length > 0 ? {
2992
+ items: cbi.subMenuItems.map((sub) => ({
2993
+ key: sub.id,
2994
+ text: sub.text,
2995
+ iconProps: sub.iconName ? { iconName: sub.iconName } : void 0,
2996
+ onClick: () => {
2997
+ if (!designMode && onButtonClick) {
2998
+ onButtonClick(sub.action);
2999
+ }
3000
+ }
3001
+ }))
3002
+ } : void 0;
3003
+ const cbiIconOnly = (cbi.iconOnly !== void 0 ? cbi.iconOnly : commandBarIconOnly) && !!cbi.iconName;
3004
+ additionalItems.push({
3005
+ key: cbi.id,
3006
+ text: cbiIconOnly ? void 0 : cbi.text,
3007
+ iconProps: cbi.iconName ? { iconName: cbi.iconName } : void 0,
3008
+ iconOnly: cbiIconOnly,
3009
+ ariaLabel: cbi.text,
3010
+ disabled: designMode || buttonDisabled,
3011
+ buttonStyles: cmdButtonStyles,
3012
+ onClick: () => {
3013
+ if (!designMode && onButtonClick) {
3014
+ onButtonClick(cbi.action);
3015
+ }
3016
+ },
3017
+ subMenuProps: cbiSubMenuProps
3018
+ });
3019
+ });
3020
+ }
3021
+ const cmdItems = [...additionalItems];
3022
+ const insertPos = Math.min(Math.max(commandBarMainPosition, 0), cmdItems.length);
3023
+ cmdItems.splice(insertPos, 0, mainButtonItem);
3024
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3025
+ import_react28.CommandBar,
3026
+ {
3027
+ items: cmdItems,
3028
+ styles: {
3029
+ root: {
3030
+ padding: 0,
3031
+ height: btnHeight,
3032
+ ...fullWidth && { width: "100%" },
3033
+ ...backgroundColor && { backgroundColor }
3034
+ }
3035
+ }
3036
+ }
3037
+ );
3038
+ }
3039
+ case "split":
3040
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3041
+ import_react28.DefaultButton,
3042
+ {
3043
+ text: displayText,
3044
+ iconProps,
3045
+ onClick: handleClick,
3046
+ disabled: designMode || buttonDisabled,
3047
+ styles: customStyles,
3048
+ split: true,
3049
+ primary: isPrimary,
3050
+ menuProps: menuProps || { items: [] }
3051
+ }
3052
+ );
3053
+ case "toggle": {
3054
+ const toggleText = toggleChecked ? checkedText || displayText : uncheckedText || displayText;
3055
+ const toggleIconProps = toggleChecked && checkedIconName ? { iconName: checkedIconName } : iconProps;
3056
+ const effectiveToggleIconOnly = iconOnly && !!(toggleIconProps?.iconName || iconName);
3057
+ const toggleBg = toggleChecked && checkedBackgroundColor ? checkedBackgroundColor : backgroundColor;
3058
+ const toggleTextCol = toggleChecked && checkedTextColor ? checkedTextColor : textColor;
3059
+ const toggleIconCol = toggleChecked ? checkedIconColor || checkedTextColor || resolvedIconColor : resolvedIconColor;
3060
+ const toggleStyles = {
3061
+ root: {
3062
+ height: btnHeight,
3063
+ minWidth: buttonSize === "small" ? 60 : void 0,
3064
+ ...fullWidth && { width: "100%" },
3065
+ ...toggleBg && { backgroundColor: toggleBg, borderColor: toggleBg },
3066
+ ...toggleTextCol && { color: toggleTextCol }
3067
+ },
3068
+ flexContainer: {
3069
+ ...iconPosition === "after" && { flexDirection: "row-reverse" }
3070
+ },
3071
+ rootChecked: {
3072
+ ...checkedBackgroundColor && {
3073
+ backgroundColor: checkedBackgroundColor,
3074
+ borderColor: checkedBackgroundColor
3075
+ },
3076
+ ...checkedTextColor && { color: checkedTextColor }
3077
+ },
3078
+ rootCheckedHovered: {
3079
+ ...checkedBackgroundColor && {
3080
+ backgroundColor: checkedBackgroundColor,
3081
+ borderColor: checkedBackgroundColor
3082
+ },
3083
+ ...checkedTextColor && { color: checkedTextColor }
3084
+ },
3085
+ label: {
3086
+ ...toggleTextCol && { color: toggleTextCol },
3087
+ ...effectiveToggleIconOnly && { display: "none" }
3088
+ },
3089
+ icon: {
3090
+ ...toggleIconCol && { color: toggleIconCol }
3091
+ }
3092
+ };
3093
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3094
+ import_react28.DefaultButton,
3095
+ {
3096
+ text: effectiveToggleIconOnly ? void 0 : toggleText,
3097
+ iconProps: toggleIconProps,
3098
+ onClick: () => {
3099
+ setToggleChecked(!toggleChecked);
3100
+ handleClick();
3101
+ },
3102
+ disabled: designMode || buttonDisabled,
3103
+ styles: toggleStyles,
3104
+ toggle: true,
3105
+ checked: toggleChecked,
3106
+ primary: isPrimary
3107
+ }
3108
+ );
3109
+ }
3110
+ default:
3111
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3112
+ import_react28.DefaultButton,
3113
+ {
3114
+ text: displayText,
3115
+ iconProps,
3116
+ onClick: handleClick,
3117
+ disabled: designMode || buttonDisabled,
3118
+ styles: customStyles,
3119
+ primary: isPrimary
3120
+ }
3121
+ );
3122
+ }
3123
+ };
3124
+ const buttonElement = tooltip ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react28.TooltipHost, { content: tooltip, children: renderButton() }) : renderButton();
3125
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { style: wrapperStyle, children: [
3126
+ buttonElement,
3127
+ designMode && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3128
+ "span",
3129
+ {
3130
+ style: {
3131
+ position: "absolute",
3132
+ top: 0,
3133
+ right: 0,
3134
+ fontSize: 9,
3135
+ padding: "1px 4px",
3136
+ backgroundColor: "#deecf9",
3137
+ color: "#0078d4",
3138
+ borderRadius: 2,
3139
+ pointerEvents: "none"
3140
+ },
3141
+ children: buttonVariant
3142
+ }
3143
+ )
3144
+ ] });
3145
+ };
3146
+
3147
+ // src/v8/controls/subgrid.tsx
3148
+ var import_react30 = require("react");
3149
+ var import_react31 = require("@fluentui/react");
3150
+
3151
+ // src/hooks.ts
3152
+ var import_react29 = require("react");
3153
+ function useLiveData(control, enabled = true) {
3154
+ const ctx = useOptionalFormRuntimeContext();
3155
+ const [records, setRecords] = (0, import_react29.useState)(null);
3156
+ const [loading, setLoading] = (0, import_react29.useState)(false);
3157
+ const [error, setError] = (0, import_react29.useState)(null);
3158
+ const [fetchXmlAttributes, setFetchXmlAttributes] = (0, import_react29.useState)([]);
3159
+ const [timelineItems, setTimelineItems] = (0, import_react29.useState)(null);
3160
+ const fetchLiveData = ctx?.fetchLiveData;
3161
+ const propertiesKey = JSON.stringify(control.properties ?? {});
3162
+ const dataBindingKey = JSON.stringify(control.dataBinding ?? {});
3163
+ (0, import_react29.useEffect)(() => {
3164
+ if (!enabled || !fetchLiveData) {
3165
+ setRecords(null);
3166
+ setLoading(false);
3167
+ setError(null);
3168
+ setTimelineItems(null);
3169
+ setFetchXmlAttributes([]);
3170
+ return;
3171
+ }
3172
+ let cancelled = false;
3173
+ const controller = new AbortController();
3174
+ setLoading(true);
3175
+ setError(null);
3176
+ fetchLiveData(control, { signal: controller.signal }).then((data) => {
3177
+ if (cancelled) return;
3178
+ setRecords(data.records);
3179
+ setFetchXmlAttributes(data.fetchXmlAttributes);
3180
+ setTimelineItems(data.timelineItems);
3181
+ setLoading(false);
3182
+ }).catch((err) => {
3183
+ if (cancelled) return;
3184
+ setError(err instanceof Error ? err.message : "Failed to fetch live data");
3185
+ setRecords(null);
3186
+ setTimelineItems(null);
3187
+ setLoading(false);
3188
+ });
3189
+ return () => {
3190
+ cancelled = true;
3191
+ controller.abort();
3192
+ };
3193
+ }, [fetchLiveData, enabled, control.type, control.name, propertiesKey, dataBindingKey]);
3194
+ return { records, loading, error, fetchXmlAttributes, timelineItems };
3195
+ }
3196
+
3197
+ // src/v8/controls/subgrid.tsx
3198
+ var import_jsx_runtime29 = require("react/jsx-runtime");
3199
+ var SubgridBranch = ({
3200
+ control,
3201
+ records: recordsProp,
3202
+ columns: columnsProp
3203
+ }) => {
3204
+ const declaredColumns = columnsProp ?? (control.properties.columns ?? []);
3205
+ const entityName = control.properties.entityName || "";
3206
+ const maxRows = control.properties.maxRows ?? 5;
3207
+ const skipFetch = recordsProp !== void 0;
3208
+ const { records: liveRecords, loading, error } = useLiveData(control, !skipFetch);
3209
+ const records = recordsProp ?? liveRecords ?? [];
3210
+ const detailsListColumns = (0, import_react30.useMemo)(
3211
+ () => declaredColumns.map((col) => ({
3212
+ key: col.key,
3213
+ name: col.name || col.fieldName,
3214
+ fieldName: col.fieldName,
3215
+ minWidth: col.minWidth ?? 80,
3216
+ maxWidth: col.maxWidth ?? (col.minWidth ?? 80) * 2,
3217
+ isResizable: col.isResizable ?? true
3218
+ })),
3219
+ [declaredColumns]
3220
+ );
3221
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { style: { border: "1px solid #edebe9", borderRadius: 2, overflow: "hidden", backgroundColor: "#fff" }, children: [
3222
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
3223
+ "div",
3224
+ {
3225
+ style: {
3226
+ display: "flex",
3227
+ alignItems: "center",
3228
+ padding: "6px 12px",
3229
+ borderBottom: "1px solid #edebe9",
3230
+ backgroundColor: "#faf9f8",
3231
+ gap: 8
3232
+ },
3233
+ children: [
3234
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_react31.Icon, { iconName: "GridViewSmall", styles: { root: { fontSize: 14, color: "#0078d4" } } }),
3235
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { style: { fontSize: 12, fontWeight: 600, color: "#323130" }, children: entityName || "Subgrid" }),
3236
+ !skipFetch && liveRecords !== null && liveRecords.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3237
+ "span",
3238
+ {
3239
+ style: {
3240
+ fontSize: 10,
3241
+ color: "#107c10",
3242
+ backgroundColor: "#dff6dd",
3243
+ padding: "2px 6px",
3244
+ borderRadius: 2
3245
+ },
3246
+ children: "Live"
3247
+ }
3248
+ ),
3249
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("span", { style: { marginLeft: "auto", fontSize: 11, color: "#605e5c" }, children: [
3250
+ "Max: ",
3251
+ maxRows
3252
+ ] })
3253
+ ]
3254
+ }
3255
+ ),
3256
+ loading && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { style: { padding: 16, textAlign: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_react31.Spinner, { size: import_react31.SpinnerSize.small, label: "Loading records...", labelPosition: "right" }) }),
3257
+ error && !loading && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
3258
+ "div",
3259
+ {
3260
+ style: {
3261
+ padding: 12,
3262
+ backgroundColor: "#fef0f0",
3263
+ color: "#a80000",
3264
+ fontSize: 12,
3265
+ borderBottom: "1px solid #edebe9"
3266
+ },
3267
+ children: [
3268
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_react31.Icon, { iconName: "Warning", styles: { root: { marginRight: 6, verticalAlign: "middle" } } }),
3269
+ error
3270
+ ]
3271
+ }
3272
+ ),
3273
+ !loading && !error && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { style: { maxHeight: Math.max(200, maxRows * 36), overflow: "auto" }, children: records.length === 0 || detailsListColumns.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { style: { padding: 24, textAlign: "center", color: "#605e5c", fontSize: 12 }, children: detailsListColumns.length === 0 ? "No columns configured" : "No records" }) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3274
+ import_react31.DetailsList,
3275
+ {
3276
+ items: records,
3277
+ columns: detailsListColumns,
3278
+ layoutMode: import_react31.DetailsListLayoutMode.justified,
3279
+ selectionMode: import_react31.SelectionMode.none,
3280
+ compact: true,
3281
+ isHeaderVisible: true
3282
+ }
3283
+ ) })
3284
+ ] });
3285
+ };
3286
+
3287
+ // src/v8/controls/timeline.tsx
3288
+ var import_react32 = require("react");
3289
+ var import_react33 = require("@fluentui/react");
3290
+ var import_jsx_runtime30 = require("react/jsx-runtime");
3291
+ var ALL_MOCK_ACTIVITIES = [
3292
+ { id: "mock-1", type: "email", icon: "Mail", title: "Email sent", description: "Re: Follow up on proposal", time: "2 hours ago", color: "#0078d4", createdon: /* @__PURE__ */ new Date(), timestamp: 19 },
3293
+ { id: "mock-2", type: "phonecall", icon: "Phone", title: "Phone call", description: "Discussed pricing options", time: "Yesterday", color: "#107c10", createdon: /* @__PURE__ */ new Date(), timestamp: 18 },
3294
+ { id: "mock-3", type: "task", icon: "TaskSolid", title: "Task completed", description: "Send product brochure", time: "3 days ago", color: "#8764b8", createdon: /* @__PURE__ */ new Date(), timestamp: 17 },
3295
+ { id: "mock-4", type: "note", icon: "QuickNote", title: "Note added", description: "Customer interested in premium plan", time: "1 week ago", color: "#ca5010", createdon: /* @__PURE__ */ new Date(), timestamp: 16 },
3296
+ { id: "mock-5", type: "appointment", icon: "Calendar", title: "Appointment scheduled", description: "Demo meeting with stakeholders", time: "2 weeks ago", color: "#038387", createdon: /* @__PURE__ */ new Date(), timestamp: 15 }
3297
+ ];
3298
+ var TimelineControl = ({ control, interactive }) => {
3299
+ const maxActivities = control.properties.maxActivities ?? 4;
3300
+ const showEmail = control.properties.showEmail ?? true;
3301
+ const showPhoneCall = control.properties.showPhoneCall ?? true;
3302
+ const showTask = control.properties.showTask ?? true;
3303
+ const showNote = control.properties.showNote ?? true;
3304
+ const showAppointment = control.properties.showAppointment ?? true;
3305
+ const sortOrder = control.properties.sortOrder ?? "newest";
3306
+ const ctx = useOptionalFormRuntimeContext();
3307
+ const hasPreviewRecord = !!ctx?.previewContext?.recordId;
3308
+ const { timelineItems, loading, error } = useLiveData(control, hasPreviewRecord);
3309
+ const useLiveDataMode = !!(timelineItems && timelineItems.length > 0);
3310
+ const isLoading = loading;
3311
+ const filteredMockActivities = (0, import_react32.useMemo)(
3312
+ () => ALL_MOCK_ACTIVITIES.filter((activity) => {
3313
+ switch (activity.type) {
3314
+ case "email":
3315
+ return showEmail;
3316
+ case "phonecall":
3317
+ return showPhoneCall;
3318
+ case "task":
3319
+ return showTask;
3320
+ case "note":
3321
+ return showNote;
3322
+ case "appointment":
3323
+ return showAppointment;
3324
+ default:
3325
+ return true;
3326
+ }
3327
+ }),
3328
+ [showEmail, showPhoneCall, showTask, showNote, showAppointment]
3329
+ );
3330
+ const sortedMockActivities = (0, import_react32.useMemo)(
3331
+ () => [...filteredMockActivities].sort(
3332
+ (a, b) => sortOrder === "newest" ? b.timestamp - a.timestamp : a.timestamp - b.timestamp
3333
+ ),
3334
+ [filteredMockActivities, sortOrder]
3335
+ );
3336
+ const mockDisplayActivities = sortedMockActivities.slice(0, maxActivities);
3337
+ const displayActivities = useLiveDataMode ? timelineItems : mockDisplayActivities;
3338
+ const activeFilters = [showEmail, showPhoneCall, showTask, showNote, showAppointment].filter(Boolean).length;
3339
+ void interactive;
3340
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { style: { border: "1px solid #edebe9", borderRadius: 2, overflow: "hidden", backgroundColor: "#fff" }, children: [
3341
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
3342
+ "div",
3343
+ {
3344
+ style: {
3345
+ display: "flex",
3346
+ alignItems: "center",
3347
+ justifyContent: "space-between",
3348
+ padding: "8px 12px",
3349
+ borderBottom: "1px solid #edebe9",
3350
+ backgroundColor: "#faf9f8"
3351
+ },
3352
+ children: [
3353
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
3354
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react33.Icon, { iconName: "TimelineProgress", styles: { root: { fontSize: 14, color: "#0078d4" } } }),
3355
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { style: { fontSize: 13, fontWeight: 600, color: "#323130" }, children: "Timeline" }),
3356
+ (useLiveDataMode || isLoading) && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3357
+ "span",
3358
+ {
3359
+ style: {
3360
+ fontSize: 10,
3361
+ color: "#107c10",
3362
+ backgroundColor: "#dff6dd",
3363
+ padding: "2px 6px",
3364
+ borderRadius: 2
3365
+ },
3366
+ children: "Live"
3367
+ }
3368
+ ),
3369
+ activeFilters < 5 && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
3370
+ "span",
3371
+ {
3372
+ style: {
3373
+ fontSize: 10,
3374
+ color: "#605e5c",
3375
+ backgroundColor: "#f3f2f1",
3376
+ padding: "2px 6px",
3377
+ borderRadius: 2
3378
+ },
3379
+ children: [
3380
+ activeFilters,
3381
+ " of 5 types"
3382
+ ]
3383
+ }
3384
+ )
3385
+ ] }),
3386
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { style: { fontSize: 10, color: "#a19f9d" }, children: sortOrder === "newest" ? "\u2193 Newest" : "\u2191 Oldest" }) })
3387
+ ]
3388
+ }
3389
+ ),
3390
+ isLoading && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { style: { padding: 16, textAlign: "center", backgroundColor: "#faf9f8" }, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react33.Spinner, { size: import_react33.SpinnerSize.small, label: "Loading activities...", labelPosition: "right" }) }),
3391
+ error && !isLoading && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
3392
+ "div",
3393
+ {
3394
+ style: {
3395
+ padding: 12,
3396
+ backgroundColor: error.includes("Select a preview record") ? "#deecf9" : "#fef0f0",
3397
+ color: error.includes("Select a preview record") ? "#0078d4" : "#a80000",
3398
+ fontSize: 12,
3399
+ borderBottom: "1px solid #edebe9"
3400
+ },
3401
+ children: [
3402
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3403
+ import_react33.Icon,
3404
+ {
3405
+ iconName: error.includes("Select a preview record") ? "Info" : "Warning",
3406
+ styles: { root: { marginRight: 6, verticalAlign: "middle" } }
3407
+ }
3408
+ ),
3409
+ error
3410
+ ]
3411
+ }
3412
+ ),
3413
+ !isLoading && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { style: { maxHeight: Math.max(200, maxActivities * 52), overflowY: "auto" }, children: displayActivities.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { style: { padding: 24, textAlign: "center", color: "#605e5c", fontSize: 12 }, children: [
3414
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3415
+ import_react33.Icon,
3416
+ {
3417
+ iconName: useLiveDataMode ? "TimelineProgress" : "Filter",
3418
+ styles: { root: { fontSize: 24, color: "#a19f9d", marginBottom: 8 } }
3419
+ }
3420
+ ),
3421
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { children: useLiveDataMode ? "No activities found for this record" : "No activities to display" })
3422
+ ] }) : displayActivities.map((activity, idx) => /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
3423
+ "div",
3424
+ {
3425
+ style: {
3426
+ display: "flex",
3427
+ padding: "10px 12px",
3428
+ borderBottom: idx < displayActivities.length - 1 ? "1px solid #edebe9" : "none",
3429
+ gap: 10
3430
+ },
3431
+ children: [
3432
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3433
+ "div",
3434
+ {
3435
+ style: {
3436
+ width: 32,
3437
+ height: 32,
3438
+ borderRadius: "50%",
3439
+ backgroundColor: activity.color,
3440
+ display: "flex",
3441
+ alignItems: "center",
3442
+ justifyContent: "center",
3443
+ flexShrink: 0
3444
+ },
3445
+ children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react33.Icon, { iconName: activity.icon, styles: { root: { fontSize: 14, color: "#fff" } } })
3446
+ }
3447
+ ),
3448
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
3449
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { style: { fontSize: 13, fontWeight: 600, color: "#323130" }, children: activity.title }),
3450
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3451
+ "div",
3452
+ {
3453
+ style: {
3454
+ fontSize: 12,
3455
+ color: "#605e5c",
3456
+ whiteSpace: "nowrap",
3457
+ overflow: "hidden",
3458
+ textOverflow: "ellipsis"
3459
+ },
3460
+ children: activity.description
3461
+ }
3462
+ )
3463
+ ] }),
3464
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { style: { fontSize: 11, color: "#a19f9d", whiteSpace: "nowrap" }, children: activity.time })
3465
+ ]
3466
+ },
3467
+ activity.id || idx
3468
+ )) }),
3469
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
3470
+ "div",
3471
+ {
3472
+ style: {
3473
+ display: "flex",
3474
+ alignItems: "center",
3475
+ justifyContent: "space-between",
3476
+ padding: "6px 12px",
3477
+ borderTop: "1px solid #edebe9",
3478
+ backgroundColor: "#faf9f8",
3479
+ fontSize: 11,
3480
+ color: "#605e5c"
3481
+ },
3482
+ children: [
3483
+ isLoading ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { children: "Loading..." }) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { children: useLiveDataMode ? `Showing ${displayActivities.length} activities` : `Showing ${displayActivities.length} of ${filteredMockActivities.length}` }),
3484
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("span", { children: [
3485
+ "Max: ",
3486
+ maxActivities
3487
+ ] })
3488
+ ]
3489
+ }
3490
+ )
3491
+ ] });
3492
+ };
3493
+
3494
+ // src/v8/controls/internals/NestedRecordsSidePanel.tsx
3495
+ var import_react36 = require("react");
3496
+ var import_react37 = require("@fluentui/react");
3497
+
3498
+ // src/v8/shared/gridCellRenderers.tsx
3499
+ var import_react34 = __toESM(require("react"), 1);
3500
+ var import_react35 = require("@fluentui/react");
3501
+ var import_jsx_runtime31 = require("react/jsx-runtime");
3502
+ function renderStatusBadge(value, config) {
3503
+ if (value == null) return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { style: { color: "#a19f9d" }, children: "-" });
3504
+ const displayMode = config.displayMode || "box";
3505
+ const iconOnly = config.iconOnly === true;
3506
+ let label;
3507
+ let color;
3508
+ let icon;
3509
+ let optionKey;
3510
+ if (typeof value === "object" && value !== null && "label" in value) {
3511
+ const opt = value;
3512
+ label = opt.label;
3513
+ color = opt.color || "#605e5c";
3514
+ icon = opt.icon;
3515
+ optionKey = opt.value;
3516
+ } else {
3517
+ label = String(value);
3518
+ color = "#605e5c";
3519
+ optionKey = value;
3520
+ }
3521
+ if (!icon) {
3522
+ const cfgOptions = config.options ?? [];
3523
+ const matched = cfgOptions.find(
3524
+ (o) => String(o.value) === String(optionKey) || o.label && o.label === label
3525
+ );
3526
+ if (matched?.icon) icon = matched.icon;
3527
+ }
3528
+ const labelHidden = iconOnly && !!icon;
3529
+ const baseStyle = {
3530
+ display: "inline-flex",
3531
+ alignItems: "center",
3532
+ gap: labelHidden ? 0 : 4,
3533
+ padding: labelHidden ? "2px 4px" : "2px 8px",
3534
+ borderRadius: 3,
3535
+ fontSize: 12,
3536
+ lineHeight: "18px",
3537
+ fontWeight: 500
3538
+ };
3539
+ const iconNode = icon ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_react35.Icon, { iconName: icon, styles: { root: { fontSize: labelHidden ? 14 : 12 } } }) : null;
3540
+ const labelNode = labelHidden ? null : /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { children: label });
3541
+ const titleAttr = labelHidden ? label : void 0;
3542
+ switch (displayMode) {
3543
+ case "box":
3544
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("span", { title: titleAttr, "aria-label": titleAttr, style: { ...baseStyle, backgroundColor: color, color: "#fff" }, children: [
3545
+ iconNode,
3546
+ labelNode
3547
+ ] });
3548
+ case "border":
3549
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("span", { title: titleAttr, "aria-label": titleAttr, style: { ...baseStyle, border: `1px solid ${color}`, color }, children: [
3550
+ iconNode,
3551
+ labelNode
3552
+ ] });
3553
+ default:
3554
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("span", { title: titleAttr, "aria-label": titleAttr, style: { ...baseStyle, color }, children: [
3555
+ iconNode,
3556
+ labelNode
3557
+ ] });
3558
+ }
3559
+ }
3560
+ function renderCurrency(value, config) {
3561
+ if (value == null) return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { style: { color: "#a19f9d" }, children: "-" });
3562
+ const currencyCode = config.currencyCode || "USD";
3563
+ const decimals = config.decimals ?? 2;
3564
+ const showTrend = !!config.showTrend;
3565
+ let amount;
3566
+ let trend;
3567
+ if (typeof value === "object" && value !== null && "amount" in value) {
3568
+ const cv = value;
3569
+ amount = cv.amount;
3570
+ trend = cv.trend;
3571
+ } else {
3572
+ amount = typeof value === "number" ? value : parseFloat(String(value)) || 0;
3573
+ }
3574
+ const symbols = { USD: "$", EUR: "\u20AC", GBP: "\xA3", JPY: "\xA5", CAD: "C$", AUD: "A$" };
3575
+ const symbol = symbols[currencyCode] || currencyCode;
3576
+ const formatted = `${symbol}${amount.toLocaleString(void 0, { minimumFractionDigits: decimals, maximumFractionDigits: decimals })}`;
3577
+ const trendIcons = {
3578
+ up: { icon: "TriangleSolidUp12", color: "#107C10" },
3579
+ down: { icon: "TriangleSolidDown12", color: "#D13438" },
3580
+ flat: { icon: "Remove", color: "#605e5c" }
3581
+ };
3582
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("span", { style: { display: "inline-flex", alignItems: "center", gap: 4 }, children: [
3583
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { children: formatted }),
3584
+ showTrend && trend && trendIcons[trend] && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3585
+ import_react35.Icon,
3586
+ {
3587
+ iconName: trendIcons[trend].icon,
3588
+ styles: { root: { fontSize: 10, color: trendIcons[trend].color } }
3589
+ }
3590
+ )
3591
+ ] });
3592
+ }
3593
+ function renderProgress(value, config) {
3594
+ const max = config.max ?? 100;
3595
+ const min = config.min ?? 0;
3596
+ const showLabel = config.showLabel !== false;
3597
+ const thresholds = config.thresholds || {};
3598
+ const numVal = typeof value === "number" ? value : parseFloat(String(value)) || 0;
3599
+ const pct = Math.max(0, Math.min(1, (numVal - min) / (max - min)));
3600
+ const pctDisplay = Math.round(pct * 100);
3601
+ let barColor = "#0078d4";
3602
+ if (thresholds.danger != null && pctDisplay < thresholds.danger) barColor = "#D13438";
3603
+ else if (thresholds.warning != null && pctDisplay < thresholds.warning) barColor = "#CA5010";
3604
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6, minWidth: 80 }, children: [
3605
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3606
+ import_react35.ProgressIndicator,
3607
+ {
3608
+ percentComplete: pct,
3609
+ barHeight: 6,
3610
+ styles: {
3611
+ root: { flex: 1 },
3612
+ progressBar: { backgroundColor: barColor }
3613
+ }
3614
+ }
3615
+ ),
3616
+ showLabel && /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("span", { style: { fontSize: 11, color: "#605e5c", minWidth: 28 }, children: [
3617
+ pctDisplay,
3618
+ "%"
3619
+ ] })
3620
+ ] });
3621
+ }
3622
+ function renderDate(value, config) {
3623
+ if (value == null) return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { style: { color: "#a19f9d" }, children: "-" });
3624
+ const format = config.format || "short";
3625
+ const showTime = config.showTime === true;
3626
+ const str = String(value);
3627
+ try {
3628
+ const date = new Date(str);
3629
+ if (isNaN(date.getTime())) return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { children: str });
3630
+ let dateText;
3631
+ switch (format) {
3632
+ case "long":
3633
+ dateText = date.toLocaleDateString(void 0, { year: "numeric", month: "long", day: "numeric" });
3634
+ break;
3635
+ case "relative": {
3636
+ const diffMs = Date.now() - date.getTime();
3637
+ const diffDays = Math.floor(diffMs / 864e5);
3638
+ if (diffDays === 0) return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { children: "Today" });
3639
+ if (diffDays === 1) return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { children: "Yesterday" });
3640
+ if (diffDays < 30) return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("span", { children: [
3641
+ diffDays,
3642
+ " days ago"
3643
+ ] });
3644
+ if (diffDays < 365) return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("span", { children: [
3645
+ Math.floor(diffDays / 30),
3646
+ " months ago"
3647
+ ] });
3648
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("span", { children: [
3649
+ Math.floor(diffDays / 365),
3650
+ " years ago"
3651
+ ] });
3652
+ }
3653
+ case "iso":
3654
+ if (showTime) {
3655
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { children: date.toISOString().slice(0, 16).replace("T", " ") });
3656
+ }
3657
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { children: date.toISOString().split("T")[0] });
3658
+ default:
3659
+ dateText = date.toLocaleDateString();
3660
+ }
3661
+ if (showTime) {
3662
+ const timeText = date.toLocaleTimeString(void 0, { hour: "2-digit", minute: "2-digit" });
3663
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { children: `${dateText} ${timeText}` });
3664
+ }
3665
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { children: dateText });
3666
+ } catch {
3667
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { children: str });
3668
+ }
3669
+ }
3670
+ function renderBoolean(value, config) {
3671
+ const boolVal = !!value;
3672
+ const displayAs = config.displayAs || "icon";
3673
+ switch (displayAs) {
3674
+ case "toggle":
3675
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_react35.Toggle, { checked: boolVal, disabled: true, styles: { root: { margin: 0 } } });
3676
+ case "text":
3677
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { children: boolVal ? config.trueLabel || "Yes" : config.falseLabel || "No" });
3678
+ default:
3679
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3680
+ import_react35.Icon,
3681
+ {
3682
+ iconName: boolVal ? "CheckMark" : "Cancel",
3683
+ styles: { root: { color: boolVal ? "#107C10" : "#D13438", fontSize: 14 } }
3684
+ }
3685
+ );
3686
+ }
3687
+ }
3688
+ function renderRating(value, config) {
3689
+ const max = config.max ?? 5;
3690
+ const numVal = typeof value === "number" ? value : parseFloat(String(value)) || 0;
3691
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3692
+ import_react35.Rating,
3693
+ {
3694
+ rating: numVal,
3695
+ max,
3696
+ size: import_react35.RatingSize.Small,
3697
+ readOnly: true,
3698
+ styles: { root: { display: "inline-flex" } }
3699
+ }
3700
+ );
3701
+ }
3702
+ function renderLookup(value, config) {
3703
+ if (value == null) return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { style: { color: "#a19f9d" }, children: "-" });
3704
+ const displayAsLink = config.displayAsLink !== false;
3705
+ const showEntityIcon = !!config.showEntityIcon;
3706
+ let name;
3707
+ let entityType;
3708
+ if (typeof value === "object" && value !== null && "name" in value) {
3709
+ const lv = value;
3710
+ name = lv.name;
3711
+ entityType = lv.entityType;
3712
+ } else {
3713
+ name = String(value);
3714
+ }
3715
+ const entityIcons = {
3716
+ account: "CityNext",
3717
+ contact: "Contact",
3718
+ opportunity: "Lightbulb",
3719
+ lead: "PeopleAdd"
3720
+ };
3721
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("span", { style: { display: "inline-flex", alignItems: "center", gap: 4 }, children: [
3722
+ showEntityIcon && entityType && entityIcons[entityType] && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_react35.Icon, { iconName: entityIcons[entityType], styles: { root: { fontSize: 12, color: "#605e5c" } } }),
3723
+ displayAsLink ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_react35.Link, { children: name }) : /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { children: name })
3724
+ ] });
3725
+ }
3726
+ function evaluateCondition(rowData, condition) {
3727
+ const fieldVal = rowData[condition.field];
3728
+ const numField = typeof fieldVal === "number" ? fieldVal : parseFloat(String(fieldVal));
3729
+ const numCondition = typeof condition.value === "number" ? condition.value : parseFloat(String(condition.value));
3730
+ switch (condition.operator) {
3731
+ case "eq":
3732
+ return fieldVal === condition.value || String(fieldVal) === String(condition.value);
3733
+ case "gt":
3734
+ return !isNaN(numField) && !isNaN(numCondition) && numField > numCondition;
3735
+ case "lt":
3736
+ return !isNaN(numField) && !isNaN(numCondition) && numField < numCondition;
3737
+ case "gte":
3738
+ return !isNaN(numField) && !isNaN(numCondition) && numField >= numCondition;
3739
+ case "lte":
3740
+ return !isNaN(numField) && !isNaN(numCondition) && numField <= numCondition;
3741
+ case "contains":
3742
+ return String(fieldVal).toLowerCase().includes(String(condition.value).toLowerCase());
3743
+ case "between": {
3744
+ const numTo = typeof condition.valueTo === "number" ? condition.valueTo : parseFloat(String(condition.valueTo));
3745
+ return !isNaN(numField) && !isNaN(numCondition) && !isNaN(numTo) && numField >= numCondition && numField <= numTo;
3746
+ }
3747
+ default:
3748
+ return false;
3749
+ }
3750
+ }
3751
+ function resolveConditionalStyle(rowData, conditionalStyles, defaultStyle) {
3752
+ if (conditionalStyles) {
3753
+ for (const rule of conditionalStyles) {
3754
+ if (evaluateCondition(rowData, rule)) {
3755
+ return rule.style;
3756
+ }
3757
+ }
3758
+ }
3759
+ return defaultStyle ?? {};
3760
+ }
3761
+ function resolveSlotValue(slot, rowData) {
3762
+ if (slot.fieldBinding) return rowData[slot.fieldBinding];
3763
+ return slot.staticValue;
3764
+ }
3765
+ function renderCompositeSlot(slot, rowData) {
3766
+ const value = resolveSlotValue(slot, rowData);
3767
+ const style = resolveConditionalStyle(rowData, slot.conditionalStyles, slot.defaultStyle);
3768
+ switch (slot.type) {
3769
+ case "icon": {
3770
+ const iconName = value != null ? String(value) : "StatusCircleQuestionMark";
3771
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_react35.Icon, { iconName, styles: { root: { fontSize: 14, color: style.color } } });
3772
+ }
3773
+ case "text":
3774
+ if (value == null) return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { style: { color: "#a19f9d", ...style }, children: "-" });
3775
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { style: { fontSize: 13, ...style }, children: String(value) });
3776
+ case "badge": {
3777
+ const label = value != null ? String(value) : "-";
3778
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { style: {
3779
+ display: "inline-block",
3780
+ padding: "1px 6px",
3781
+ borderRadius: 3,
3782
+ fontSize: 11,
3783
+ fontWeight: 500,
3784
+ backgroundColor: "#edebe9",
3785
+ ...style
3786
+ }, children: label });
3787
+ }
3788
+ case "link":
3789
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_react35.Link, { style, children: value != null ? String(value) : "-" });
3790
+ case "image": {
3791
+ const src = value != null ? String(value) : "";
3792
+ return src ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("img", { src, alt: "", style: { height: 20, borderRadius: 2, ...style } }) : null;
3793
+ }
3794
+ case "spacer":
3795
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { style: { flex: 1, ...style } });
3796
+ case "progress": {
3797
+ const numVal = typeof value === "number" ? value : parseFloat(String(value)) || 0;
3798
+ const pct = Math.max(0, Math.min(1, numVal / 100));
3799
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3800
+ import_react35.ProgressIndicator,
3801
+ {
3802
+ percentComplete: pct,
3803
+ barHeight: 4,
3804
+ styles: { root: { flex: 1, minWidth: 40 }, progressBar: { backgroundColor: style.backgroundColor } }
3805
+ }
3806
+ );
3807
+ }
3808
+ case "rating": {
3809
+ const numVal = typeof value === "number" ? value : parseFloat(String(value)) || 0;
3810
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_react35.Rating, { rating: numVal, max: 5, size: import_react35.RatingSize.Small, readOnly: true, styles: { root: { display: "inline-flex" } } });
3811
+ }
3812
+ default:
3813
+ return value != null ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { style, children: String(value) }) : null;
3814
+ }
3815
+ }
3816
+ function renderComposite(value, config, rowData) {
3817
+ const compositeConfig = config;
3818
+ if (!compositeConfig?.slots?.length) {
3819
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { style: { color: "#a19f9d", fontSize: 11 }, children: "No slots configured" });
3820
+ }
3821
+ const row = rowData || (typeof value === "object" && value !== null ? value : {});
3822
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { style: {
3823
+ display: "flex",
3824
+ flexDirection: compositeConfig.layout === "vertical" ? "column" : "row",
3825
+ alignItems: compositeConfig.layout === "vertical" ? "flex-start" : "center",
3826
+ gap: compositeConfig.gap ?? 4
3827
+ }, children: compositeConfig.slots.map((slot) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_react34.default.Fragment, { children: renderCompositeSlot(slot, row) }, slot.id)) });
3828
+ }
3829
+ function renderGridCell(value, colDef, rowData) {
3830
+ const { rendererType, rendererConfig } = colDef;
3831
+ switch (rendererType) {
3832
+ case "optionset":
3833
+ return renderStatusBadge(value, rendererConfig);
3834
+ case "currency":
3835
+ return renderCurrency(value, rendererConfig);
3836
+ case "progress":
3837
+ return renderProgress(value, rendererConfig);
3838
+ case "date":
3839
+ return renderDate(value, rendererConfig);
3840
+ case "boolean":
3841
+ return renderBoolean(value, rendererConfig);
3842
+ case "rating":
3843
+ return renderRating(value, rendererConfig);
3844
+ case "lookup":
3845
+ return renderLookup(value, rendererConfig);
3846
+ case "composite":
3847
+ return renderComposite(value, rendererConfig, rowData);
3848
+ case "text":
3849
+ default:
3850
+ if (value == null) return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { style: { color: "#a19f9d" }, children: "-" });
3851
+ if (typeof value === "object") return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { children: JSON.stringify(value) });
3852
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { children: String(value) });
3853
+ }
3854
+ }
3855
+
3856
+ // src/v8/controls/internals/NestedRecordsSidePanel.tsx
3857
+ var import_jsx_runtime32 = require("react/jsx-runtime");
3858
+ var PANEL_TYPE_BY_SIZE = {
3859
+ small: import_react37.PanelType.smallFixedFar,
3860
+ medium: import_react37.PanelType.medium,
3861
+ large: import_react37.PanelType.large
3862
+ };
3863
+ var headerSubtitleClass = (0, import_react37.mergeStyles)({
3864
+ fontSize: 12,
3865
+ color: "#605e5c",
3866
+ marginTop: 2
3867
+ });
3868
+ var emptyStateClass = (0, import_react37.mergeStyles)({
3869
+ padding: 24,
3870
+ textAlign: "center",
3871
+ color: "#605e5c"
3872
+ });
3873
+ var NestedRecordsSidePanel = ({
3874
+ isOpen,
3875
+ onDismiss,
3876
+ childGridDef,
3877
+ parentLabel,
3878
+ panelSize = "medium",
3879
+ rowCount,
3880
+ generateMockData
3881
+ }) => {
3882
+ const visibleColumns = (0, import_react36.useMemo)(() => {
3883
+ if (!childGridDef) return [];
3884
+ return [...childGridDef.columns].filter((c) => c.isVisible).sort((a, b) => a.order - b.order);
3885
+ }, [childGridDef]);
3886
+ const items = (0, import_react36.useMemo)(() => {
3887
+ if (!childGridDef || !generateMockData) return [];
3888
+ const count = rowCount ?? childGridDef.pageSize ?? 5;
3889
+ return generateMockData(visibleColumns, count);
3890
+ }, [childGridDef, rowCount, visibleColumns, generateMockData]);
3891
+ const detailsListColumns = (0, import_react36.useMemo)(
3892
+ () => visibleColumns.map((col) => ({
3893
+ key: col.id,
3894
+ name: col.displayName || col.fieldName,
3895
+ fieldName: col.fieldName,
3896
+ minWidth: col.minWidth ?? 50,
3897
+ maxWidth: col.maxWidth ?? 200,
3898
+ isResizable: true,
3899
+ onRender: (item) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_jsx_runtime32.Fragment, { children: renderGridCell(item[col.fieldName], col) })
3900
+ })),
3901
+ [visibleColumns]
3902
+ );
3903
+ const headerText = childGridDef?.name ?? "Related records";
3904
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3905
+ import_react37.Panel,
3906
+ {
3907
+ isOpen,
3908
+ onDismiss,
3909
+ type: PANEL_TYPE_BY_SIZE[panelSize] ?? import_react37.PanelType.medium,
3910
+ headerText,
3911
+ closeButtonAriaLabel: "Close",
3912
+ isLightDismiss: true,
3913
+ onRenderHeader: () => /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_react37.Stack, { tokens: { childrenGap: 2 }, styles: { root: { padding: "12px 24px 0 24px" } }, children: [
3914
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_react37.Text, { variant: "xLarge", styles: { root: { fontWeight: 600 } }, children: headerText }),
3915
+ parentLabel ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("span", { className: headerSubtitleClass, children: [
3916
+ "for ",
3917
+ parentLabel
3918
+ ] }) : null
3919
+ ] }),
3920
+ onRenderFooterContent: () => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_react37.Stack, { horizontal: true, horizontalAlign: "end", tokens: { childrenGap: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_react37.DefaultButton, { onClick: onDismiss, children: "Close" }) }),
3921
+ isFooterAtBottom: true,
3922
+ children: !childGridDef ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: emptyStateClass, children: "No related grid is configured for this row." }) : visibleColumns.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: emptyStateClass, children: "The related grid has no visible columns." }) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3923
+ import_react37.DetailsList,
3924
+ {
3925
+ items,
3926
+ columns: detailsListColumns,
3927
+ layoutMode: import_react37.DetailsListLayoutMode.justified,
3928
+ selectionMode: import_react37.SelectionMode.multiple,
3929
+ compact: childGridDef.compactMode,
3930
+ isHeaderVisible: true
3931
+ }
3932
+ )
3933
+ }
3934
+ );
3935
+ };
3936
+
3937
+ // src/v8/controls/internals/NestedRecordsCallout.tsx
3938
+ var import_react38 = require("react");
3939
+ var import_react39 = require("@fluentui/react");
3940
+ var import_jsx_runtime33 = require("react/jsx-runtime");
3941
+ var calloutContentClass = (0, import_react39.mergeStyles)({
3942
+ width: 320,
3943
+ padding: 12,
3944
+ display: "flex",
3945
+ flexDirection: "column",
3946
+ gap: 8
3947
+ });
3948
+ var calloutHeaderClass = (0, import_react39.mergeStyles)({
3949
+ display: "flex",
3950
+ alignItems: "center",
3951
+ justifyContent: "space-between",
3952
+ fontSize: 12,
3953
+ fontWeight: 600,
3954
+ color: "#323130",
3955
+ textTransform: "uppercase",
3956
+ letterSpacing: "0.5px"
3957
+ });
3958
+ var calloutRowClass = (0, import_react39.mergeStyles)({
3959
+ display: "flex",
3960
+ flexDirection: "column",
3961
+ padding: "6px 0",
3962
+ borderTop: "1px solid #edebe9",
3963
+ fontSize: 12,
3964
+ color: "#323130",
3965
+ gap: 2
3966
+ });
3967
+ var calloutPrimaryClass = (0, import_react39.mergeStyles)({
3968
+ fontWeight: 500,
3969
+ overflow: "hidden",
3970
+ textOverflow: "ellipsis",
3971
+ whiteSpace: "nowrap"
3972
+ });
3973
+ var calloutSecondaryClass = (0, import_react39.mergeStyles)({
3974
+ fontSize: 11,
3975
+ color: "#605e5c",
3976
+ overflow: "hidden",
3977
+ textOverflow: "ellipsis",
3978
+ whiteSpace: "nowrap"
3979
+ });
3980
+ var calloutFooterClass = (0, import_react39.mergeStyles)({
3981
+ display: "flex",
3982
+ justifyContent: "flex-end",
3983
+ paddingTop: 4,
3984
+ borderTop: "1px solid #edebe9"
3985
+ });
3986
+ var emptyStateClass2 = (0, import_react39.mergeStyles)({
3987
+ fontSize: 12,
3988
+ color: "#605e5c",
3989
+ fontStyle: "italic"
3990
+ });
3991
+ var NestedRecordsCallout = ({
3992
+ childGridDef,
3993
+ hoverDelay = 300,
3994
+ maxRows = 5,
3995
+ onViewAll,
3996
+ children,
3997
+ generateMockData
3998
+ }) => {
3999
+ const [isOpen, setIsOpen] = (0, import_react38.useState)(false);
4000
+ const targetRef = (0, import_react38.useRef)(null);
4001
+ const hoverTimer = (0, import_react38.useRef)(null);
4002
+ const dismissTimer = (0, import_react38.useRef)(null);
4003
+ const DISMISS_DELAY_MS = 200;
4004
+ const cancelHoverTimer = (0, import_react38.useCallback)(() => {
4005
+ if (hoverTimer.current) {
4006
+ clearTimeout(hoverTimer.current);
4007
+ hoverTimer.current = null;
4008
+ }
4009
+ }, []);
4010
+ const cancelDismissTimer = (0, import_react38.useCallback)(() => {
4011
+ if (dismissTimer.current) {
4012
+ clearTimeout(dismissTimer.current);
4013
+ dismissTimer.current = null;
4014
+ }
4015
+ }, []);
4016
+ const startDismiss = (0, import_react38.useCallback)(() => {
4017
+ cancelDismissTimer();
4018
+ dismissTimer.current = setTimeout(() => setIsOpen(false), DISMISS_DELAY_MS);
4019
+ }, [cancelDismissTimer]);
4020
+ const handleTriggerEnter = (0, import_react38.useCallback)(() => {
4021
+ cancelDismissTimer();
4022
+ cancelHoverTimer();
4023
+ hoverTimer.current = setTimeout(() => setIsOpen(true), hoverDelay);
4024
+ }, [cancelDismissTimer, cancelHoverTimer, hoverDelay]);
4025
+ const handleTriggerLeave = (0, import_react38.useCallback)(() => {
4026
+ cancelHoverTimer();
4027
+ startDismiss();
4028
+ }, [cancelHoverTimer, startDismiss]);
4029
+ const closeNow = (0, import_react38.useCallback)(() => {
4030
+ cancelHoverTimer();
4031
+ cancelDismissTimer();
4032
+ setIsOpen(false);
4033
+ }, [cancelHoverTimer, cancelDismissTimer]);
4034
+ (0, import_react38.useEffect)(() => () => {
4035
+ cancelHoverTimer();
4036
+ cancelDismissTimer();
4037
+ }, [cancelHoverTimer, cancelDismissTimer]);
4038
+ const visibleColumns = childGridDef ? [...childGridDef.columns].filter((c) => c.isVisible).sort((a, b) => a.order - b.order) : [];
4039
+ const previewItems = childGridDef && generateMockData ? generateMockData(visibleColumns, maxRows) : [];
4040
+ const primaryCol = visibleColumns[0];
4041
+ const secondaryCol = visibleColumns[1];
4042
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
4043
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
4044
+ "span",
4045
+ {
4046
+ ref: targetRef,
4047
+ onMouseEnter: handleTriggerEnter,
4048
+ onMouseLeave: handleTriggerLeave,
4049
+ children
4050
+ }
4051
+ ),
4052
+ isOpen && targetRef.current ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
4053
+ import_react39.Callout,
4054
+ {
4055
+ target: targetRef.current,
4056
+ directionalHint: import_react39.DirectionalHint.bottomLeftEdge,
4057
+ isBeakVisible: false,
4058
+ gapSpace: 4,
4059
+ onDismiss: closeNow,
4060
+ setInitialFocus: false,
4061
+ preventDismissOnLostFocus: true,
4062
+ preventDismissOnScroll: true,
4063
+ styles: { root: { borderRadius: 4 } },
4064
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
4065
+ "div",
4066
+ {
4067
+ className: calloutContentClass,
4068
+ onMouseEnter: cancelDismissTimer,
4069
+ onMouseLeave: startDismiss,
4070
+ children: [
4071
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: calloutHeaderClass, children: [
4072
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { children: childGridDef?.name ?? "Related records" }),
4073
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { style: { fontWeight: 400, color: "#605e5c" }, children: previewItems.length })
4074
+ ] }),
4075
+ !childGridDef || previewItems.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: emptyStateClass2, children: "No related records to preview." }) : previewItems.map((item, idx) => /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: calloutRowClass, children: [
4076
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: calloutPrimaryClass, children: primaryCol ? renderGridCell(item[primaryCol.fieldName], primaryCol) : "\u2014" }),
4077
+ secondaryCol ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: calloutSecondaryClass, children: renderGridCell(item[secondaryCol.fieldName], secondaryCol) }) : null
4078
+ ] }, idx)),
4079
+ onViewAll ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: calloutFooterClass, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
4080
+ import_react39.Link,
4081
+ {
4082
+ onClick: () => {
4083
+ closeNow();
4084
+ onViewAll();
4085
+ },
4086
+ children: "View all \u2192"
4087
+ }
4088
+ ) }) : null
4089
+ ]
4090
+ }
4091
+ )
4092
+ }
4093
+ ) : null
4094
+ ] });
4095
+ };
4096
+
4097
+ // src/v8/controls/internals/NestedRecordsTrigger.tsx
4098
+ var import_react40 = require("react");
4099
+ var import_react41 = require("@fluentui/react");
4100
+ var import_jsx_runtime34 = require("react/jsx-runtime");
4101
+ var triggerWrapperClass = (0, import_react41.mergeStyles)({
4102
+ marginTop: 6,
4103
+ display: "flex",
4104
+ alignItems: "center",
4105
+ gap: 4
4106
+ });
4107
+ var badgeClass = (0, import_react41.mergeStyles)({
4108
+ display: "inline-flex",
4109
+ alignItems: "center",
4110
+ gap: 4,
4111
+ padding: "2px 8px",
4112
+ borderRadius: 12,
4113
+ backgroundColor: "#deecf9",
4114
+ color: "#0078d4",
4115
+ fontSize: 11,
4116
+ fontWeight: 500,
4117
+ cursor: "pointer",
4118
+ selectors: {
4119
+ ":hover": { backgroundColor: "#c7e0f4" }
4120
+ }
4121
+ });
4122
+ var detailPaneClass = (0, import_react41.mergeStyles)({
4123
+ marginTop: 8,
4124
+ borderTop: "1px solid #edebe9",
4125
+ paddingTop: 8
4126
+ });
4127
+ var detailPaneHeaderClass = (0, import_react41.mergeStyles)({
4128
+ display: "flex",
4129
+ alignItems: "center",
4130
+ justifyContent: "space-between",
4131
+ fontSize: 11,
4132
+ color: "#605e5c",
4133
+ marginBottom: 4,
4134
+ textTransform: "uppercase",
4135
+ letterSpacing: "0.4px",
4136
+ fontWeight: 600
4137
+ });
4138
+ var DEFAULT_TRIGGER_LABEL = "View {count} related";
4139
+ var DEFAULT_TRIGGER_ICON = "OpenPaneMirrored";
4140
+ function formatTriggerLabel(label, count) {
4141
+ const template = label && label.trim().length > 0 ? label : DEFAULT_TRIGGER_LABEL;
4142
+ return template.replace(/\{count\}/g, String(count));
4143
+ }
4144
+ var NestedRecordsTrigger = ({
4145
+ childGridDef,
4146
+ config,
4147
+ parentLabel,
4148
+ childCount,
4149
+ generateMockData
4150
+ }) => {
4151
+ const [panelOpen, setPanelOpen] = (0, import_react40.useState)(false);
4152
+ const advertisedCount = (0, import_react40.useMemo)(() => {
4153
+ if (typeof childCount === "number") return childCount;
4154
+ return childGridDef?.pageSize ?? 5;
4155
+ }, [childCount, childGridDef]);
4156
+ const label = formatTriggerLabel(config.triggerLabel, advertisedCount);
4157
+ const iconName = config.triggerIcon && config.triggerIcon.trim().length > 0 ? config.triggerIcon : DEFAULT_TRIGGER_ICON;
4158
+ if (config.mode === "detail-pane") {
4159
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: detailPaneClass, children: [
4160
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: detailPaneHeaderClass, children: [
4161
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { children: childGridDef?.name ?? "Related records" }),
4162
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { style: { fontWeight: 400 }, children: advertisedCount })
4163
+ ] }),
4164
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4165
+ NestedDetailPaneList,
4166
+ {
4167
+ childGridDef,
4168
+ rowCount: advertisedCount,
4169
+ generateMockData
4170
+ }
4171
+ )
4172
+ ] });
4173
+ }
4174
+ if (config.mode === "side-panel") {
4175
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
4176
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: triggerWrapperClass, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4177
+ import_react41.DefaultButton,
4178
+ {
4179
+ iconProps: { iconName },
4180
+ text: label,
4181
+ onClick: (e) => {
4182
+ e.stopPropagation();
4183
+ setPanelOpen(true);
4184
+ },
4185
+ styles: { root: { height: 28, padding: "0 8px", fontSize: 12 } }
4186
+ }
4187
+ ) }),
4188
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4189
+ NestedRecordsSidePanel,
4190
+ {
4191
+ isOpen: panelOpen,
4192
+ onDismiss: () => setPanelOpen(false),
4193
+ childGridDef,
4194
+ parentLabel,
4195
+ panelSize: config.panelSize,
4196
+ generateMockData
4197
+ }
4198
+ )
4199
+ ] });
4200
+ }
4201
+ if (config.mode === "hover-callout") {
4202
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
4203
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: triggerWrapperClass, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4204
+ NestedRecordsCallout,
4205
+ {
4206
+ childGridDef,
4207
+ hoverDelay: config.hoverDelay,
4208
+ maxRows: config.calloutMaxRows,
4209
+ onViewAll: () => setPanelOpen(true),
4210
+ generateMockData,
4211
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { className: badgeClass, children: [
4212
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react41.Icon, { iconName, styles: { root: { fontSize: 11 } } }),
4213
+ label
4214
+ ] })
4215
+ }
4216
+ ) }),
4217
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4218
+ NestedRecordsSidePanel,
4219
+ {
4220
+ isOpen: panelOpen,
4221
+ onDismiss: () => setPanelOpen(false),
4222
+ childGridDef,
4223
+ parentLabel,
4224
+ panelSize: config.panelSize,
4225
+ generateMockData
4226
+ }
4227
+ )
4228
+ ] });
4229
+ }
4230
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
4231
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: triggerWrapperClass, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4232
+ import_react41.DefaultButton,
4233
+ {
4234
+ iconProps: { iconName },
4235
+ text: label,
4236
+ onClick: (e) => {
4237
+ e.stopPropagation();
4238
+ setPanelOpen(true);
4239
+ },
4240
+ styles: { root: { height: 28, padding: "0 8px", fontSize: 12 } }
4241
+ }
4242
+ ) }),
4243
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4244
+ NestedRecordsSidePanel,
4245
+ {
4246
+ isOpen: panelOpen,
4247
+ onDismiss: () => setPanelOpen(false),
4248
+ childGridDef,
4249
+ parentLabel,
4250
+ generateMockData
4251
+ }
4252
+ )
4253
+ ] });
4254
+ };
4255
+ var NestedDetailPaneList = ({
4256
+ childGridDef,
4257
+ rowCount,
4258
+ generateMockData
4259
+ }) => {
4260
+ const visibleColumns = (0, import_react40.useMemo)(() => {
4261
+ if (!childGridDef) return [];
4262
+ return [...childGridDef.columns].filter((c) => c.isVisible).sort((a, b) => a.order - b.order);
4263
+ }, [childGridDef]);
4264
+ const items = (0, import_react40.useMemo)(
4265
+ () => childGridDef && generateMockData ? generateMockData(visibleColumns, rowCount) : [],
4266
+ [childGridDef, rowCount, visibleColumns, generateMockData]
4267
+ );
4268
+ const columns = (0, import_react40.useMemo)(
4269
+ () => visibleColumns.map((col) => ({
4270
+ key: col.id,
4271
+ name: col.displayName || col.fieldName,
4272
+ fieldName: col.fieldName,
4273
+ minWidth: col.minWidth ?? 50,
4274
+ maxWidth: col.maxWidth ?? 160,
4275
+ isResizable: true,
4276
+ onRender: (item) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_jsx_runtime34.Fragment, { children: renderGridCell(item[col.fieldName], col) })
4277
+ })),
4278
+ [visibleColumns]
4279
+ );
4280
+ if (!childGridDef || visibleColumns.length === 0) {
4281
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { style: { fontSize: 11, color: "#a19f9d", fontStyle: "italic", padding: "4px 0" }, children: "No related grid configured." });
4282
+ }
4283
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4284
+ import_react41.DetailsList,
4285
+ {
4286
+ items,
4287
+ columns,
4288
+ layoutMode: import_react41.DetailsListLayoutMode.justified,
4289
+ selectionMode: import_react41.SelectionMode.none,
4290
+ compact: true,
4291
+ isHeaderVisible: true
4292
+ }
4293
+ );
4294
+ };
4295
+
4296
+ // src/v8/controls/internals/FocusedViewMasterDetailPreview.tsx
4297
+ var import_react42 = require("react");
4298
+ var import_react43 = require("@fluentui/react");
4299
+ var import_jsx_runtime35 = require("react/jsx-runtime");
4300
+ var containerClass3 = (0, import_react43.mergeStyles)({
4301
+ border: "1px solid #edebe9",
4302
+ borderRadius: 2,
4303
+ overflow: "hidden",
4304
+ backgroundColor: "#fff",
4305
+ display: "flex",
4306
+ height: 460
4307
+ });
4308
+ var railClass = (0, import_react43.mergeStyles)({
4309
+ width: 320,
4310
+ flexShrink: 0,
4311
+ display: "flex",
4312
+ flexDirection: "column",
4313
+ borderRight: "1px solid #edebe9"
4314
+ });
4315
+ var railHeaderClass = (0, import_react43.mergeStyles)({
4316
+ display: "flex",
4317
+ alignItems: "center",
4318
+ padding: "10px 12px",
4319
+ borderBottom: "1px solid #edebe9"
4320
+ });
4321
+ var railHeaderTextClass = (0, import_react43.mergeStyles)({
4322
+ fontSize: 14,
4323
+ fontWeight: 600,
4324
+ color: "#323130",
4325
+ flex: 1
4326
+ });
4327
+ var railSearchClass = (0, import_react43.mergeStyles)({
4328
+ padding: "8px 12px",
4329
+ borderBottom: "1px solid #edebe9",
4330
+ flexShrink: 0
4331
+ });
4332
+ var railListClass = (0, import_react43.mergeStyles)({
4333
+ flex: 1,
4334
+ overflowY: "auto"
4335
+ });
4336
+ var cardClass2 = (0, import_react43.mergeStyles)({
4337
+ display: "flex",
4338
+ alignItems: "flex-start",
4339
+ padding: "10px 12px",
4340
+ cursor: "pointer",
4341
+ borderBottom: "1px solid #edebe9",
4342
+ borderLeft: "3px solid transparent",
4343
+ transition: "background-color 0.1s",
4344
+ ":hover": {
4345
+ backgroundColor: "#f3f2f1"
4346
+ }
4347
+ });
4348
+ var cardSelectedClass = (0, import_react43.mergeStyles)({
4349
+ backgroundColor: "#cce4f6",
4350
+ borderLeftColor: "#0078d4",
4351
+ ":hover": {
4352
+ backgroundColor: "#cce4f6"
4353
+ }
4354
+ });
4355
+ var cardContentClass = (0, import_react43.mergeStyles)({
4356
+ flex: 1,
4357
+ minWidth: 0,
4358
+ marginLeft: 10
4359
+ });
4360
+ var cardPrimaryNameClass = (0, import_react43.mergeStyles)({
4361
+ fontSize: 14,
4362
+ fontWeight: 600,
4363
+ color: "#323130",
4364
+ whiteSpace: "nowrap",
4365
+ overflow: "hidden",
4366
+ textOverflow: "ellipsis"
4367
+ });
4368
+ var cardRowClass = (0, import_react43.mergeStyles)({
4369
+ fontSize: 12,
4370
+ color: "#605e5c",
4371
+ whiteSpace: "nowrap",
4372
+ overflow: "hidden",
4373
+ textOverflow: "ellipsis",
4374
+ lineHeight: "18px"
4375
+ });
4376
+ var railFooterClass = (0, import_react43.mergeStyles)({
4377
+ padding: "8px 12px",
4378
+ borderTop: "1px solid #edebe9",
4379
+ fontSize: 12,
4380
+ color: "#605e5c",
4381
+ textAlign: "center",
4382
+ flexShrink: 0
4383
+ });
4384
+ var detailPaneClass2 = (0, import_react43.mergeStyles)({
4385
+ flex: 1,
4386
+ display: "flex",
4387
+ flexDirection: "column",
4388
+ overflow: "hidden"
4389
+ });
4390
+ var detailHeaderClass = (0, import_react43.mergeStyles)({
4391
+ padding: "12px 16px",
4392
+ borderBottom: "1px solid #edebe9",
4393
+ flexShrink: 0
4394
+ });
4395
+ var detailHeaderTitleClass = (0, import_react43.mergeStyles)({
4396
+ fontSize: 16,
4397
+ fontWeight: 600,
4398
+ color: "#323130"
4399
+ });
4400
+ var detailHeaderSubtitleClass = (0, import_react43.mergeStyles)({
4401
+ fontSize: 12,
4402
+ color: "#605e5c",
4403
+ marginTop: 2
4404
+ });
4405
+ var detailBodyClass = (0, import_react43.mergeStyles)({
4406
+ flex: 1,
4407
+ overflow: "auto"
4408
+ });
4409
+ var emptyStateClass3 = (0, import_react43.mergeStyles)({
4410
+ flex: 1,
4411
+ display: "flex",
4412
+ alignItems: "center",
4413
+ justifyContent: "center",
4414
+ color: "#605e5c",
4415
+ fontSize: 13,
4416
+ padding: 32,
4417
+ textAlign: "center"
4418
+ });
4419
+ var upNextClass = (0, import_react43.mergeStyles)({
4420
+ padding: "8px 12px",
4421
+ borderTop: "1px solid #edebe9",
4422
+ display: "flex",
4423
+ alignItems: "center",
4424
+ gap: 8,
4425
+ fontSize: 11,
4426
+ color: "#605e5c"
4427
+ });
4428
+ var FocusedViewMasterDetailPreview = ({
4429
+ cards,
4430
+ focusedViewConfig,
4431
+ entityName,
4432
+ showRelatedRecords = false,
4433
+ hideSearchBox = false,
4434
+ liveLoading = false,
4435
+ childGridDef,
4436
+ getInitials: getInitials3,
4437
+ getPersonaColor: getPersonaColor2,
4438
+ showUpNextActivity = false,
4439
+ generateMockData
4440
+ }) => {
4441
+ const [selectedCardId, setSelectedCardId] = (0, import_react42.useState)(null);
4442
+ (0, import_react42.useEffect)(() => {
4443
+ if (cards.length === 0) {
4444
+ setSelectedCardId(null);
4445
+ return;
4446
+ }
4447
+ if (selectedCardId && !cards.some((c) => c.id === selectedCardId)) {
4448
+ setSelectedCardId(null);
4449
+ }
4450
+ }, [cards, selectedCardId]);
4451
+ const selectedCard = (0, import_react42.useMemo)(
4452
+ () => cards.find((c) => c.id === selectedCardId) ?? null,
4453
+ [cards, selectedCardId]
4454
+ );
4455
+ const selectedDisplayName = selectedCard?.rows[0]?.primaryValue ?? "";
4456
+ const childVisibleColumns = (0, import_react42.useMemo)(
4457
+ () => [...childGridDef.columns].filter((c) => c.isVisible).sort((a, b) => a.order - b.order),
4458
+ [childGridDef.columns]
4459
+ );
4460
+ const childItemsByParent = (0, import_react42.useMemo)(() => {
4461
+ const out = {};
4462
+ if (!generateMockData) return out;
4463
+ const childCount = childGridDef.pageSize ?? 4;
4464
+ cards.forEach((card, idx) => {
4465
+ out[card.id] = generateMockData(childVisibleColumns, childCount + idx % 2);
4466
+ });
4467
+ return out;
4468
+ }, [cards, childVisibleColumns, childGridDef.pageSize, generateMockData]);
4469
+ const childDetailsListColumns = (0, import_react42.useMemo)(
4470
+ () => childVisibleColumns.map((col) => ({
4471
+ key: col.id,
4472
+ name: col.displayName || col.fieldName,
4473
+ fieldName: col.fieldName,
4474
+ minWidth: col.minWidth ?? 80,
4475
+ maxWidth: col.maxWidth ?? 200,
4476
+ isResizable: true,
4477
+ onRender: (item) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_jsx_runtime35.Fragment, { children: renderGridCell(item[col.fieldName], col) })
4478
+ })),
4479
+ [childVisibleColumns]
4480
+ );
4481
+ const childRows = selectedCard ? childItemsByParent[selectedCard.id] ?? [] : [];
4482
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: containerClass3, children: [
4483
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: railClass, children: [
4484
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: railHeaderClass, children: [
4485
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { className: railHeaderTextClass, children: [
4486
+ entityName,
4487
+ showRelatedRecords && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { style: {
4488
+ fontSize: 10,
4489
+ color: "#0078d4",
4490
+ backgroundColor: "#deecf9",
4491
+ padding: "2px 6px",
4492
+ borderRadius: 2,
4493
+ marginLeft: 8,
4494
+ fontWeight: 400
4495
+ }, children: "Related" })
4496
+ ] }),
4497
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_react43.Icon, { iconName: "ChevronDown", styles: { root: { fontSize: 12, color: "#605e5c" } } })
4498
+ ] }),
4499
+ !hideSearchBox && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: railSearchClass, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
4500
+ import_react43.SearchBox,
4501
+ {
4502
+ placeholder: "Search this view",
4503
+ styles: { root: { borderRadius: 4 } }
4504
+ }
4505
+ ) }),
4506
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: railListClass, children: cards.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { style: { padding: 32, textAlign: "center", color: "#605e5c", fontSize: 13 }, children: "No records to preview" }) : cards.map((card) => {
4507
+ const isSelected = card.id === selectedCardId;
4508
+ const primaryName = card.rows[0]?.primaryValue || "Record";
4509
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
4510
+ "div",
4511
+ {
4512
+ role: "option",
4513
+ "aria-selected": isSelected,
4514
+ tabIndex: 0,
4515
+ onClick: () => setSelectedCardId((prev) => prev === card.id ? null : card.id),
4516
+ onKeyDown: (e) => {
4517
+ if (e.key === "Enter" || e.key === " ") {
4518
+ e.preventDefault();
4519
+ setSelectedCardId((prev) => prev === card.id ? null : card.id);
4520
+ } else if (e.key === "Escape") {
4521
+ setSelectedCardId(null);
4522
+ }
4523
+ },
4524
+ className: `${cardClass2} ${isSelected ? cardSelectedClass : ""}`,
4525
+ children: [
4526
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
4527
+ import_react43.Persona,
4528
+ {
4529
+ text: primaryName,
4530
+ imageInitials: getInitials3(primaryName),
4531
+ size: import_react43.PersonaSize.size32,
4532
+ initialsColor: getPersonaColor2(primaryName),
4533
+ hidePersonaDetails: true
4534
+ }
4535
+ ),
4536
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: cardContentClass, children: [
4537
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: cardPrimaryNameClass, title: primaryName, children: primaryName }),
4538
+ card.rows.slice(1).map((row, rowIdx) => {
4539
+ const label = focusedViewConfig.rows[rowIdx + 1]?.primaryField.label;
4540
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: cardRowClass, children: label ? `${label}: ${row.primaryValue}` : row.primaryValue }, rowIdx);
4541
+ })
4542
+ ] })
4543
+ ]
4544
+ },
4545
+ card.id
4546
+ );
4547
+ }) }),
4548
+ showUpNextActivity && /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: upNextClass, children: [
4549
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_react43.Icon, { iconName: "CheckboxComposite", styles: { root: { fontSize: 12 } } }),
4550
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { children: "Up next activity" }),
4551
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { style: { marginLeft: "auto" }, children: "00:00 AM" })
4552
+ ] }),
4553
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: railFooterClass, children: liveLoading ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_react43.Spinner, { size: import_react43.SpinnerSize.xSmall, label: "Loading...", labelPosition: "right" }) : /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { children: [
4554
+ cards.length > 0 ? `1-${cards.length}` : "0-0",
4555
+ " of ",
4556
+ cards.length
4557
+ ] }) })
4558
+ ] }),
4559
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: detailPaneClass2, children: selectedCard ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
4560
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: detailHeaderClass, children: [
4561
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: detailHeaderTitleClass, children: selectedDisplayName }),
4562
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: detailHeaderSubtitleClass, children: childGridDef.name })
4563
+ ] }),
4564
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: detailBodyClass, children: childVisibleColumns.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: emptyStateClass3, children: "The related grid has no visible columns." }) : /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
4565
+ import_react43.DetailsList,
4566
+ {
4567
+ items: childRows,
4568
+ columns: childDetailsListColumns,
4569
+ layoutMode: import_react43.DetailsListLayoutMode.justified,
4570
+ selectionMode: import_react43.SelectionMode.multiple,
4571
+ compact: childGridDef.compactMode,
4572
+ isHeaderVisible: true
4573
+ }
4574
+ ) })
4575
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: emptyStateClass3, children: [
4576
+ "Select a record to view related ",
4577
+ childGridDef.name
4578
+ ] }) })
4579
+ ] });
4580
+ };
4581
+ // Annotate the CommonJS export names for ESM import in node:
4582
+ 0 && (module.exports = {
4583
+ ButtonControl,
4584
+ ChartBranch,
4585
+ ChartRenderer,
4586
+ CheckboxControl,
4587
+ ChoiceGroupBranch,
4588
+ ChoiceGroupControl,
4589
+ ComboBoxControl,
4590
+ DateControl,
4591
+ DropdownControl,
4592
+ EmojiRating,
4593
+ FocusedViewMasterDetailPreview,
4594
+ FormLinkControl,
4595
+ LabelControl,
4596
+ LookupBranch,
4597
+ LookupControl,
4598
+ NestedRecordsCallout,
4599
+ NestedRecordsSidePanel,
4600
+ NestedRecordsTrigger,
4601
+ NumberControl,
4602
+ PersonaControl,
4603
+ RatingControl,
4604
+ SliderControl,
4605
+ SpacerControl,
4606
+ SpinButtonControl,
4607
+ SubgridBranch,
4608
+ TextControl,
4609
+ TextFieldWithFormat,
4610
+ TextareaControl,
4611
+ TimelineControl,
4612
+ ToggleControl,
4613
+ UnknownControl,
4614
+ WebResourceBranch,
4615
+ WebResourceRenderer,
4616
+ isChartControlType,
4617
+ isDisabled,
4618
+ isEmbeddedEntityBrowser,
4619
+ lookupEntityHelpers,
4620
+ renderGridCell
4621
+ });
4622
+ //# sourceMappingURL=v8.cjs.map