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