@bpmn-io/form-js-playground 1.20.1 → 1.21.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.
@@ -585,7 +585,7 @@
585
585
  *
586
586
  * @return {Object} map with { attrValue => [ a, b, c ] }
587
587
  */
588
- function groupBy(collection, extractor, grouped = {}) {
588
+ function groupBy$1(collection, extractor, grouped = {}) {
589
589
  extractor = toExtractor(extractor);
590
590
  forEach(collection, function (val) {
591
591
  let discriminator = extractor(val) || '_';
@@ -600,7 +600,7 @@
600
600
  function uniqueBy(extractor, ...collections) {
601
601
  extractor = toExtractor(extractor);
602
602
  let grouped = {};
603
- forEach(collections, c => groupBy(c, extractor, grouped));
603
+ forEach(collections, c => groupBy$1(c, extractor, grouped));
604
604
  let result = map(grouped, function (val, key) {
605
605
  return val[0];
606
606
  });
@@ -53226,16 +53226,38 @@ Please report this to https://github.com/markedjs/marked.`, e) {
53226
53226
  * @returns {any}
53227
53227
  */
53228
53228
  evaluate(expression, data = {}) {
53229
- if (!expression) {
53229
+ if (!this.isExpression(expression)) {
53230
53230
  return null;
53231
53231
  }
53232
- if (!isString$2(expression) || !expression.startsWith('=')) {
53232
+ try {
53233
+ const {
53234
+ value: result
53235
+ } = evaluate$1(expression.slice(1), data);
53236
+ return result;
53237
+ } catch (error) {
53238
+ this._eventBus.fire('error', {
53239
+ error
53240
+ });
53241
+ return null;
53242
+ }
53243
+ }
53244
+
53245
+ /**
53246
+ * Evaluate a unary test expression. Returns null for invalid/missing expressions.
53247
+ *
53248
+ * @param {string} expression
53249
+ * @param {import('../../types').Data} [data]
53250
+ *
53251
+ * @returns {boolean|null}
53252
+ */
53253
+ evaluateUnaryTest(expression, data = {}) {
53254
+ if (!this.isExpression(expression)) {
53233
53255
  return null;
53234
53256
  }
53235
53257
  try {
53236
53258
  const {
53237
53259
  value: result
53238
- } = evaluate$1(expression.slice(1), data);
53260
+ } = unaryTest(expression.slice(1), data);
53239
53261
  return result;
53240
53262
  } catch (error) {
53241
53263
  this._eventBus.fire('error', {
@@ -53678,31 +53700,53 @@ Please report this to https://github.com/markedjs/marked.`, e) {
53678
53700
  /**
53679
53701
  * If the value is a valid expression, it is evaluated and returned. Otherwise, it is returned as-is.
53680
53702
  *
53681
- * @param {any} expressionLanguage - The expression language to use.
53703
+ * @param {import('../types').ExpressionLanguage} expressionLanguage - The expression language to use.
53682
53704
  * @param {any} value - The static value or expression to evaluate.
53683
53705
  * @param {Object} expressionContextInfo - The context information to use.
53684
53706
  * @returns {any} - Evaluated value or the original value if not an expression.
53685
53707
  */
53686
53708
  function runExpressionEvaluation(expressionLanguage, value, expressionContextInfo) {
53687
- if (expressionLanguage && expressionLanguage.isExpression(value)) {
53709
+ if (expressionLanguage.isExpression(value)) {
53688
53710
  return expressionLanguage.evaluate(value, buildExpressionContext(expressionContextInfo));
53689
53711
  }
53690
53712
  return value;
53691
53713
  }
53692
53714
 
53693
53715
  /**
53694
- * Evaluate if condition is met reactively based on the conditionChecker and form data.
53716
+ * Evaluate a value as a unary test expression. Returns null for invalid/missing expressions or
53717
+ * if the expression language is not available.
53718
+ *
53719
+ * @param {import('../types').ExpressionLanguage} expressionLanguage - The expression language to use.
53720
+ * @param {string} value - The unary test expression to evaluate.
53721
+ * @param {Object} expressionContextInfo - The context information to use.
53722
+ * @returns {boolean | null} - Evaluated result, or null if expression is invalid/missing.
53723
+ */
53724
+ function runUnaryTestEvaluation(expressionLanguage, value, expressionContextInfo) {
53725
+ return expressionLanguage.evaluateUnaryTest(value, buildExpressionContext(expressionContextInfo));
53726
+ }
53727
+
53728
+ /**
53729
+ * Evaluate a unary test expression reactively. Returns null for invalid/missing expressions.
53730
+ * The function is memoized to minimize re-renders.
53731
+ *
53732
+ * @param {string | undefined} value - A unary test expression to evaluate.
53733
+ * @returns {boolean | null} - Evaluated result, or null if expression is invalid/missing.
53734
+ */
53735
+ function useUnaryTestEvaluation(value) {
53736
+ const expressionLanguage = useService$2('expressionLanguage');
53737
+ const expressionContextInfo = q$2(LocalExpressionContext);
53738
+ return F$2(() => runUnaryTestEvaluation(expressionLanguage, value, expressionContextInfo), [expressionLanguage, expressionContextInfo, value]);
53739
+ }
53740
+
53741
+ /**
53742
+ * Evaluate if condition is met reactively based on the expression language and form data.
53695
53743
  *
53696
53744
  * @param {string | undefined} condition
53697
53745
  *
53698
- * @returns {boolean} true if condition is met or no condition or condition checker exists
53746
+ * @returns {boolean | null} true if condition is met, false if not, null if no condition or expression language
53699
53747
  */
53700
53748
  function useCondition(condition) {
53701
- const conditionChecker = useService$2('conditionChecker', false);
53702
- const expressionContextInfo = q$2(LocalExpressionContext);
53703
- return F$2(() => {
53704
- return conditionChecker ? conditionChecker.check(condition, buildExpressionContext(expressionContextInfo)) : null;
53705
- }, [conditionChecker, condition, expressionContextInfo]);
53749
+ return useUnaryTestEvaluation(condition);
53706
53750
  }
53707
53751
 
53708
53752
  /**
@@ -54188,16 +54232,16 @@ Please report this to https://github.com/markedjs/marked.`, e) {
54188
54232
  */
54189
54233
  function useReadonly(formField, properties = {}) {
54190
54234
  const expressionLanguage = useService$2('expressionLanguage');
54191
- const conditionChecker = useService$2('conditionChecker', false);
54192
- const expressionContextInfo = q$2(LocalExpressionContext);
54193
54235
  const {
54194
54236
  readonly
54195
54237
  } = formField;
54238
+ const isExpression = expressionLanguage && expressionLanguage.isExpression(readonly);
54239
+ const evaluatedReadonly = useUnaryTestEvaluation(isExpression ? readonly : undefined);
54196
54240
  if (properties.readOnly) {
54197
54241
  return true;
54198
54242
  }
54199
- if (expressionLanguage && expressionLanguage.isExpression(readonly)) {
54200
- return conditionChecker ? conditionChecker.check(readonly, buildExpressionContext(expressionContextInfo)) : false;
54243
+ if (isExpression) {
54244
+ return evaluatedReadonly === true;
54201
54245
  }
54202
54246
  return readonly || false;
54203
54247
  }
@@ -59049,7 +59093,8 @@ Please report this to https://github.com/markedjs/marked.`, e) {
59049
59093
 
59050
59094
  /**
59051
59095
  * @typedef DocumentEndpointBuilder
59052
- * @property {(document: DocumentMetadata) => string} buildUrl
59096
+ * @property {(document: DocumentMetadata) => string} [buildUrl]
59097
+ * @property {(document: DocumentMetadata) => RequestInit|undefined} [buildRequestInit]
59053
59098
  */
59054
59099
 
59055
59100
  /**
@@ -59100,13 +59145,18 @@ Please report this to https://github.com/markedjs/marked.`, e) {
59100
59145
  class: `fjs-${type$k}-document-container`,
59101
59146
  id: domId,
59102
59147
  children: data.map((document, index) => {
59103
- const finalEndpoint = tryCatch(() => documentEndpointBuilder?.buildUrl(document)) ?? document.endpoint;
59104
- return isValidDocumentEndpoint(finalEndpoint) ? o(DocumentRenderer, {
59148
+ const finalEndpoint = tryCatch(() => documentEndpointBuilder?.buildUrl?.(document)) ?? document.endpoint;
59149
+ if (!isValidDocumentEndpoint(finalEndpoint)) {
59150
+ return null;
59151
+ }
59152
+ const requestInit = getDocumentRequestInit(documentEndpointBuilder, document);
59153
+ return o(DocumentRenderer, {
59105
59154
  documentMetadata: document,
59106
59155
  endpoint: finalEndpoint,
59156
+ requestInit: requestInit,
59107
59157
  maxHeight: maxHeight,
59108
59158
  domId: `${domId}-${index}`
59109
- }, document.documentId) : null;
59159
+ }, document.documentId);
59110
59160
  })
59111
59161
  }), o(Errors, {
59112
59162
  id: errorMessageId,
@@ -59182,13 +59232,15 @@ Please report this to https://github.com/markedjs/marked.`, e) {
59182
59232
  * @param {string} props.fileName
59183
59233
  * @param {Function} props.onError
59184
59234
  * @param {string} props.errorMessageId
59235
+ * @param {RequestInit|undefined} props.requestInit
59185
59236
  * @returns {import("preact").JSX.Element}
59186
59237
  */
59187
59238
  function PdfRenderer(props) {
59188
59239
  const {
59189
59240
  url,
59190
59241
  onError,
59191
- errorMessageId
59242
+ errorMessageId,
59243
+ requestInit
59192
59244
  } = props;
59193
59245
  /** @type {ReturnType<typeof import("preact/hooks").useState<null | string>>} */
59194
59246
  const [pdfObjectUrl, setPdfObjectUrl] = h(null);
@@ -59198,7 +59250,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
59198
59250
  let objectUrl = null;
59199
59251
  const fetchPdf = async () => {
59200
59252
  try {
59201
- const response = await fetch(url);
59253
+ const response = await fetch(url, requestInit);
59202
59254
  if (!response.ok) {
59203
59255
  setHasError(true);
59204
59256
  onError();
@@ -59218,7 +59270,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
59218
59270
  URL.revokeObjectURL(objectUrl);
59219
59271
  }
59220
59272
  };
59221
- }, [url, onError]);
59273
+ }, [url, onError, requestInit]);
59222
59274
  return o(k$3, {
59223
59275
  children: [pdfObjectUrl !== null ? o("embed", {
59224
59276
  src: pdfObjectUrl,
@@ -59231,12 +59283,61 @@ Please report this to https://github.com/markedjs/marked.`, e) {
59231
59283
  });
59232
59284
  }
59233
59285
 
59286
+ /**
59287
+ * @param {Object} props
59288
+ * @param {string} props.url
59289
+ * @param {string} props.alt
59290
+ * @param {Function} props.onError
59291
+ * @param {RequestInit|undefined} props.requestInit
59292
+ * @returns {import("preact").JSX.Element}
59293
+ */
59294
+ function ImageRenderer(props) {
59295
+ const {
59296
+ url,
59297
+ alt,
59298
+ onError,
59299
+ requestInit
59300
+ } = props;
59301
+ /** @type {ReturnType<typeof import("preact/hooks").useState<null | string>>} */
59302
+ const [imageObjectUrl, setImageObjectUrl] = h(null);
59303
+ p(() => {
59304
+ /** @type {null | string} */
59305
+ let objectUrl = null;
59306
+ const fetchImage = async () => {
59307
+ try {
59308
+ const response = await fetch(url, requestInit);
59309
+ if (!response.ok) {
59310
+ onError();
59311
+ return;
59312
+ }
59313
+ const blob = await response.blob();
59314
+ objectUrl = URL.createObjectURL(blob);
59315
+ setImageObjectUrl(objectUrl);
59316
+ } catch {
59317
+ onError();
59318
+ }
59319
+ };
59320
+ fetchImage();
59321
+ return () => {
59322
+ if (objectUrl) {
59323
+ URL.revokeObjectURL(objectUrl);
59324
+ }
59325
+ };
59326
+ }, [url, onError, requestInit]);
59327
+ return imageObjectUrl !== null ? o("img", {
59328
+ src: imageObjectUrl,
59329
+ alt: alt,
59330
+ class: `fjs-${type$k}-image`
59331
+ }) : null;
59332
+ }
59333
+
59234
59334
  /**
59235
59335
  *
59236
59336
  * @param {Object} props
59237
59337
  * @param {DocumentMetadata} props.documentMetadata
59238
59338
  * @param {string} props.endpoint
59239
59339
  * @param {string} props.domId
59340
+ * @param {RequestInit|undefined} props.requestInit
59240
59341
  * @param {number|undefined} props.maxHeight
59241
59342
  *
59242
59343
  * @returns {import("preact").JSX.Element}
@@ -59246,7 +59347,8 @@ Please report this to https://github.com/markedjs/marked.`, e) {
59246
59347
  documentMetadata,
59247
59348
  endpoint,
59248
59349
  maxHeight,
59249
- domId
59350
+ domId,
59351
+ requestInit
59250
59352
  } = props;
59251
59353
  const {
59252
59354
  metadata
@@ -59265,13 +59367,15 @@ Please report this to https://github.com/markedjs/marked.`, e) {
59265
59367
  maxHeight
59266
59368
  },
59267
59369
  "aria-describedby": hasError ? errorMessageId : undefined,
59268
- children: [o("img", {
59269
- src: endpoint,
59370
+ children: [o(ImageRenderer, {
59371
+ url: endpoint,
59270
59372
  alt: metadata.fileName,
59271
- class: `fjs-${type$k}-image`
59373
+ requestInit: requestInit,
59374
+ onError: () => setHasError(true)
59272
59375
  }), o(DownloadButton, {
59273
59376
  endpoint: endpoint,
59274
59377
  fileName: metadata.fileName,
59378
+ requestInit: requestInit,
59275
59379
  onDownloadError: () => {
59276
59380
  setHasError(true);
59277
59381
  }
@@ -59291,6 +59395,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
59291
59395
  children: o(PdfRenderer, {
59292
59396
  url: endpoint,
59293
59397
  fileName: metadata.fileName,
59398
+ requestInit: requestInit,
59294
59399
  onError: () => setHasError(true),
59295
59400
  errorMessageId: errorMessageId
59296
59401
  })
@@ -59311,6 +59416,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
59311
59416
  }), o(DownloadButton, {
59312
59417
  endpoint: endpoint,
59313
59418
  fileName: metadata.fileName,
59419
+ requestInit: requestInit,
59314
59420
  onDownloadError: () => {
59315
59421
  setHasError(true);
59316
59422
  }
@@ -59323,6 +59429,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
59323
59429
  * @param {string} props.endpoint
59324
59430
  * @param {string} props.fileName
59325
59431
  * @param {Function} props.onDownloadError
59432
+ * @param {RequestInit|undefined} props.requestInit
59326
59433
  *
59327
59434
  * @returns {import("preact").JSX.Element}
59328
59435
  */
@@ -59330,11 +59437,12 @@ Please report this to https://github.com/markedjs/marked.`, e) {
59330
59437
  const {
59331
59438
  endpoint,
59332
59439
  fileName,
59333
- onDownloadError
59440
+ onDownloadError,
59441
+ requestInit
59334
59442
  } = props;
59335
59443
  const handleDownload = async () => {
59336
59444
  try {
59337
- const response = await fetch(endpoint);
59445
+ const response = await fetch(endpoint, requestInit);
59338
59446
  if (!response.ok) {
59339
59447
  onDownloadError();
59340
59448
  return;
@@ -59388,6 +59496,16 @@ Please report this to https://github.com/markedjs/marked.`, e) {
59388
59496
  return isInViewport;
59389
59497
  }
59390
59498
 
59499
+ /**
59500
+ * @param {DocumentEndpointBuilder | null} documentEndpointBuilder
59501
+ * @param {DocumentMetadata} document
59502
+ * @returns {RequestInit|undefined}
59503
+ */
59504
+ function getDocumentRequestInit(documentEndpointBuilder, document) {
59505
+ const requestInit = tryCatch(() => documentEndpointBuilder?.buildRequestInit?.(document));
59506
+ return requestInit !== null && typeof requestInit === 'object' ? requestInit : undefined;
59507
+ }
59508
+
59391
59509
  /**
59392
59510
  * @template T
59393
59511
  * @param {() => T} fn - Function to execute
@@ -59560,6 +59678,8 @@ Please report this to https://github.com/markedjs/marked.`, e) {
59560
59678
 
59561
59679
  /**
59562
59680
  * @typedef { import('../types').Schema } Schema
59681
+ * @typedef { import('../types').ExpressionLanguage } ExpressionLanguage
59682
+ * @typedef { import('../types').Templating } Templating
59563
59683
  */
59564
59684
 
59565
59685
  /**
@@ -59582,8 +59702,8 @@ Please report this to https://github.com/markedjs/marked.`, e) {
59582
59702
  *
59583
59703
  * @param {Schema} schema
59584
59704
  * @param {object} [options]
59585
- * @param {any} [options.expressionLanguage]
59586
- * @param {any} [options.templating]
59705
+ * @param {ExpressionLanguage} [options.expressionLanguage]
59706
+ * @param {Templating} [options.templating]
59587
59707
  * @param {any} [options.formFields]
59588
59708
  * @param {boolean} [options.inputs=true]
59589
59709
  * @param {boolean} [options.outputs=true]
@@ -59682,15 +59802,26 @@ Please report this to https://github.com/markedjs/marked.`, e) {
59682
59802
  return ids;
59683
59803
  };
59684
59804
 
59805
+ /**
59806
+ * @typedef { import('../../types').ExpressionLanguage } ExpressionLanguage
59807
+ */
59808
+
59685
59809
  /**
59686
59810
  * @typedef {object} Condition
59687
59811
  * @property {string} [hide]
59688
59812
  */
59689
59813
 
59690
59814
  class ConditionChecker {
59691
- constructor(formFieldRegistry, pathRegistry, eventBus) {
59815
+ /**
59816
+ * @param {Object} formFieldRegistry
59817
+ * @param {Object} pathRegistry
59818
+ * @param {ExpressionLanguage} expressionLanguage
59819
+ * @param {Object} eventBus
59820
+ */
59821
+ constructor(formFieldRegistry, pathRegistry, expressionLanguage, eventBus) {
59692
59822
  this._formFieldRegistry = formFieldRegistry;
59693
59823
  this._pathRegistry = pathRegistry;
59824
+ this._expressionLanguage = expressionLanguage;
59694
59825
  this._eventBus = eventBus;
59695
59826
  }
59696
59827
 
@@ -59813,24 +59944,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
59813
59944
  * @returns {boolean|null}
59814
59945
  */
59815
59946
  check(condition, data = {}) {
59816
- if (!condition) {
59817
- return null;
59818
- }
59819
- if (!isString$2(condition) || !condition.startsWith('=')) {
59820
- return null;
59821
- }
59822
- try {
59823
- // cut off initial '='
59824
- const {
59825
- value: result
59826
- } = unaryTest(condition.slice(1), data);
59827
- return result;
59828
- } catch (error) {
59829
- this._eventBus.fire('error', {
59830
- error
59831
- });
59832
- return null;
59833
- }
59947
+ return this._expressionLanguage.evaluateUnaryTest(condition, data);
59834
59948
  }
59835
59949
 
59836
59950
  /**
@@ -59864,7 +59978,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
59864
59978
  return Array.isArray(parentObject) && (!parentObject.length || parentObject.every(item => item === undefined));
59865
59979
  }
59866
59980
  }
59867
- ConditionChecker.$inject = ['formFieldRegistry', 'pathRegistry', 'eventBus'];
59981
+ ConditionChecker.$inject = ['formFieldRegistry', 'pathRegistry', 'expressionLanguage', 'eventBus'];
59868
59982
  const ExpressionLanguageModule = {
59869
59983
  __init__: ['expressionLanguage', 'templating', 'conditionChecker'],
59870
59984
  expressionLanguage: ['type', FeelExpressionLanguage],
@@ -61391,6 +61505,11 @@ Please report this to https://github.com/markedjs/marked.`, e) {
61391
61505
  function invokeFunction$1(fn, args) {
61392
61506
  return fn.apply(null, args);
61393
61507
  }
61508
+
61509
+ /**
61510
+ * @typedef { import('../types').ExpressionLanguage } ExpressionLanguage
61511
+ */
61512
+
61394
61513
  const EMAIL_PATTERN = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
61395
61514
  const PHONE_PATTERN = /(\+|00)(297|93|244|1264|358|355|376|971|54|374|1684|1268|61|43|994|257|32|229|226|880|359|973|1242|387|590|375|501|1441|591|55|1246|673|975|267|236|1|61|41|56|86|225|237|243|242|682|57|269|238|506|53|5999|61|1345|357|420|49|253|1767|45|1809|1829|1849|213|593|20|291|212|34|372|251|358|679|500|33|298|691|241|44|995|44|233|350|224|590|220|245|240|30|1473|299|502|594|1671|592|852|504|385|509|36|62|44|91|246|353|98|964|354|972|39|1876|44|962|81|76|77|254|996|855|686|1869|82|383|965|856|961|231|218|1758|423|94|266|370|352|371|853|590|212|377|373|261|960|52|692|389|223|356|95|382|976|1670|258|222|1664|596|230|265|60|262|264|687|227|672|234|505|683|31|47|977|674|64|968|92|507|64|51|63|680|675|48|1787|1939|850|351|595|970|689|974|262|40|7|250|966|249|221|65|500|4779|677|232|503|378|252|508|381|211|239|597|421|386|46|268|1721|248|963|1649|235|228|66|992|690|993|670|676|1868|216|90|688|886|255|256|380|598|1|998|3906698|379|1784|58|1284|1340|84|678|681|685|967|27|260|263)(9[976]\d|8[987530]\d|6[987]\d|5[90]\d|42\d|3[875]\d|2[98654321]\d|9[8543210]|8[6421]|6[6543210]|5[87654321]|4[987654310]|3[9643210]|2[70]|7|1)\d{4,20}$/;
61396
61515
  const VALIDATE_FEEL_PROPERTIES = ['min', 'max', 'minLength', 'maxLength'];
@@ -61519,6 +61638,12 @@ Please report this to https://github.com/markedjs/marked.`, e) {
61519
61638
  }
61520
61639
  return errors;
61521
61640
  }
61641
+
61642
+ /**
61643
+ * @param {Object} validate
61644
+ * @param {ExpressionLanguage} expressionLanguage
61645
+ * @param {Object} expressionContextInfo
61646
+ */
61522
61647
  function evaluateFEELValues(validate, expressionLanguage, expressionContextInfo) {
61523
61648
  const evaluatedValidate = {
61524
61649
  ...validate
@@ -61531,6 +61656,13 @@ Please report this to https://github.com/markedjs/marked.`, e) {
61531
61656
  });
61532
61657
  return evaluatedValidate;
61533
61658
  }
61659
+
61660
+ /**
61661
+ * @param {Object} validate
61662
+ * @param {ExpressionLanguage} expressionLanguage
61663
+ * @param {Object} conditionChecker
61664
+ * @param {Object} form
61665
+ */
61534
61666
  function oldEvaluateFEELValues(validate, expressionLanguage, conditionChecker, form) {
61535
61667
  const evaluatedValidate = {
61536
61668
  ...validate
@@ -62159,7 +62291,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
62159
62291
  // helpers //////
62160
62292
 
62161
62293
  function groupByRow(components, ids) {
62162
- return groupBy(components, c => {
62294
+ return groupBy$1(components, c => {
62163
62295
  // mitigate missing row by creating new (handle legacy)
62164
62296
  const {
62165
62297
  layout
@@ -69664,7 +69796,26 @@ Please report this to https://github.com/markedjs/marked.`, e) {
69664
69796
  const fillsAndSeparators = F$2(() => {
69665
69797
  return buildFills(groups, fillRoot, separatorFn);
69666
69798
  }, [groups, fillRoot, separatorFn]);
69667
- return fillsAndSeparators;
69799
+
69800
+ // Framework-agnostic fills from SlotFillManager
69801
+ const editorContext = q$2(FormEditorContext);
69802
+ const slotFillManager = editorContext ? editorContext.getService('slotFillManager', false) : null;
69803
+ const eventBus = editorContext ? editorContext.getService('eventBus', false) : null;
69804
+ const [managerFills, setManagerFills] = h([]);
69805
+ p(() => {
69806
+ if (!eventBus || !slotFillManager) {
69807
+ return;
69808
+ }
69809
+ setManagerFills(slotFillManager.getFills(name));
69810
+ const onChange = () => setManagerFills(slotFillManager.getFills(name));
69811
+ eventBus.on('slotFillManager.changed', onChange);
69812
+ return () => eventBus.off('slotFillManager.changed', onChange);
69813
+ }, [eventBus, slotFillManager, name]);
69814
+ return o(k$3, {
69815
+ children: [fillsAndSeparators, managerFills.map(fill => o(FillContainer, {
69816
+ fill: fill
69817
+ }, fill.fillId))]
69818
+ });
69668
69819
  };
69669
69820
 
69670
69821
  /**
@@ -69677,6 +69828,34 @@ Please report this to https://github.com/markedjs/marked.`, e) {
69677
69828
  children: fill.children
69678
69829
  }, fill.id);
69679
69830
 
69831
+ /**
69832
+ * Mounts a single SlotFillManager fill's render callback into a DOM container.
69833
+ */
69834
+ function FillContainer({
69835
+ fill
69836
+ }) {
69837
+ const containerRef = _$2(null);
69838
+ const cleanupRef = _$2(null);
69839
+ p(() => {
69840
+ const container = containerRef.current;
69841
+ if (!container) {
69842
+ return;
69843
+ }
69844
+ cleanupRef.current = fill.render(container) || null;
69845
+ return () => {
69846
+ if (typeof cleanupRef.current === 'function') {
69847
+ cleanupRef.current();
69848
+ cleanupRef.current = null;
69849
+ }
69850
+ container.innerHTML = '';
69851
+ };
69852
+ }, [fill]);
69853
+ return o("div", {
69854
+ ref: containerRef,
69855
+ "data-slot-fill": fill.fillId
69856
+ });
69857
+ }
69858
+
69680
69859
  /**
69681
69860
  * Creates an array of fills, with separators inserted between groups.
69682
69861
  *
@@ -70546,6 +70725,8 @@ Please report this to https://github.com/markedjs/marked.`, e) {
70546
70725
  children: "Drag and drop components here to start designing."
70547
70726
  }), o("span", {
70548
70727
  children: "Use the preview window to test your form."
70728
+ }), o(Slot, {
70729
+ name: "editor-empty-state__footer"
70549
70730
  })]
70550
70731
  })
70551
70732
  });
@@ -82446,9 +82627,162 @@ Please report this to https://github.com/markedjs/marked.`, e) {
82446
82627
  }
82447
82628
  }
82448
82629
  RenderInjector.$inject = ['eventBus'];
82630
+
82631
+ /**
82632
+ * Framework-agnostic service for managing slot fills.
82633
+ *
82634
+ * Fills are registered as render callbacks: `(container: HTMLElement) => (() => void) | void`.
82635
+ * The optional return value is a cleanup function called when the fill is removed or the slot unmounts.
82636
+ *
82637
+ * @example
82638
+ *
82639
+ * // Via config (simplest):
82640
+ * new FormEditor({
82641
+ * slots: {
82642
+ * 'editor-empty-state__footer': (container) => {
82643
+ * container.textContent = 'Hello from vanilla JS';
82644
+ * }
82645
+ * }
82646
+ * });
82647
+ *
82648
+ * // Via config (multiple fills per slot):
82649
+ * new FormEditor({
82650
+ * slots: {
82651
+ * 'editor-empty-state__footer': [
82652
+ * (container) => { container.textContent = 'First'; },
82653
+ * { render: (container) => { container.textContent = 'Second'; }, priority: 10 }
82654
+ * ]
82655
+ * }
82656
+ * });
82657
+ *
82658
+ * // Via service (runtime):
82659
+ * const slotFillManager = editor.get('slotFillManager');
82660
+ * slotFillManager.addFill('editor-empty-state__footer', 'my-fill', {
82661
+ * render: (container) => { ... },
82662
+ * priority: 10,
82663
+ * group: 'custom'
82664
+ * });
82665
+ */
82666
+ class SlotFillManager {
82667
+ /**
82668
+ * @param {Object} slotsConfig
82669
+ * @param {import('../../core/EventBus').EventBus} eventBus
82670
+ */
82671
+ constructor(slotsConfig, eventBus) {
82672
+ this._eventBus = eventBus;
82673
+
82674
+ /** @type {Array<{ slotName: string, fillId: string, render: Function, priority: number, group: string }>} */
82675
+ this._fills = [];
82676
+ this._populateFromConfig(slotsConfig);
82677
+ }
82678
+
82679
+ /**
82680
+ * Register a fill for a named slot.
82681
+ *
82682
+ * @param {string} slotName - The slot to fill.
82683
+ * @param {string} fillId - Unique identifier for this fill.
82684
+ * @param {Function|Object} options - A render callback `(container) => cleanup`, or `{ render, priority?, group? }`.
82685
+ */
82686
+ addFill(slotName, fillId, options) {
82687
+ const fill = normalizeFill(slotName, fillId, options);
82688
+ this._fills = [...this._fills.filter(f => f.fillId !== fillId), fill];
82689
+ this._eventBus.fire('slotFillManager.changed');
82690
+ }
82691
+
82692
+ /**
82693
+ * Remove a fill by its ID.
82694
+ *
82695
+ * @param {string} fillId
82696
+ */
82697
+ removeFill(fillId) {
82698
+ const remaining = this._fills.filter(f => f.fillId !== fillId);
82699
+ if (remaining.length === this._fills.length) {
82700
+ return;
82701
+ }
82702
+ this._fills = remaining;
82703
+ this._eventBus.fire('slotFillManager.changed');
82704
+ }
82705
+
82706
+ /**
82707
+ * Get fills for a given slot, sorted by group (alphabetical) then priority (descending).
82708
+ *
82709
+ * @param {string} slotName
82710
+ * @returns {Array<{ slotName: string, fillId: string, render: Function, priority: number, group: string }>}
82711
+ */
82712
+ getFills(slotName) {
82713
+ const matching = this._fills.filter(f => f.slotName === slotName);
82714
+ return sortFills(matching);
82715
+ }
82716
+
82717
+ /**
82718
+ * @private
82719
+ */
82720
+ _populateFromConfig(slotsConfig) {
82721
+ Object.entries(slotsConfig || {}).forEach(([slotName, value]) => {
82722
+ if (Array.isArray(value)) {
82723
+ value.forEach((entry, index) => {
82724
+ this.addFill(slotName, `config__${slotName}_${index}`, entry);
82725
+ });
82726
+ } else {
82727
+ this.addFill(slotName, `config__${slotName}`, value);
82728
+ }
82729
+ });
82730
+ }
82731
+ }
82732
+ SlotFillManager.$inject = ['config.slots', 'eventBus'];
82733
+
82734
+ // helpers //////////
82735
+
82736
+ /**
82737
+ * @param {string} slotName
82738
+ * @param {string} fillId
82739
+ * @param {Function|Object} options
82740
+ * @returns {{ slotName: string, fillId: string, render: Function, priority: number, group: string }}
82741
+ */
82742
+ function normalizeFill(slotName, fillId, options) {
82743
+ if (typeof options === 'function') {
82744
+ return {
82745
+ slotName,
82746
+ fillId,
82747
+ render: options,
82748
+ priority: 0,
82749
+ group: 'z_default'
82750
+ };
82751
+ }
82752
+ const {
82753
+ render,
82754
+ priority = 0,
82755
+ group = 'z_default'
82756
+ } = options;
82757
+ return {
82758
+ slotName,
82759
+ fillId,
82760
+ render,
82761
+ priority,
82762
+ group
82763
+ };
82764
+ }
82765
+
82766
+ /**
82767
+ * Sort fills by group (alphabetical) then by priority (descending) within each group.
82768
+ */
82769
+ function sortFills(fills) {
82770
+ const grouped = groupBy(fills, f => f.group);
82771
+ return Object.keys(grouped).sort().flatMap(key => grouped[key].toSorted((a, b) => b.priority - a.priority));
82772
+ }
82773
+ function groupBy(items, keyFn) {
82774
+ return items.reduce((groups, item) => {
82775
+ const key = keyFn(item);
82776
+ return {
82777
+ ...groups,
82778
+ [key]: [...(groups[key] || []), item]
82779
+ };
82780
+ }, {});
82781
+ }
82449
82782
  const RenderInjectionModule = {
82450
- __init__: ['renderInjector'],
82451
- renderInjector: ['type', RenderInjector]
82783
+ __init__: ['renderInjector', 'slotFillManager'],
82784
+ renderInjector: ['type', RenderInjector],
82785
+ slotFillManager: ['type', SlotFillManager]
82452
82786
  };
82453
82787
  var _path;
82454
82788
  function _extends() {
@@ -84627,6 +84961,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
84627
84961
  editorProperties,
84628
84962
  viewerAdditionalModules,
84629
84963
  editorAdditionalModules,
84964
+ editorSlots,
84630
84965
  propertiesPanel: propertiesPanelConfig,
84631
84966
  apiLinkTarget,
84632
84967
  onInit
@@ -84694,7 +85029,10 @@ Please report this to https://github.com/markedjs/marked.`, e) {
84694
85029
  ...(editorProperties || {}),
84695
85030
  ariaLabel: 'Form Definition'
84696
85031
  },
84697
- additionalModules: [...(additionalModules || []), ...(editorAdditionalModules || [])]
85032
+ additionalModules: [...(additionalModules || []), ...(editorAdditionalModules || [])],
85033
+ ...(editorSlots ? {
85034
+ slots: editorSlots
85035
+ } : {})
84698
85036
  });
84699
85037
  formEditor.on('formField.add', ({
84700
85038
  formField
@@ -84757,7 +85095,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
84757
85095
  formViewer.destroy();
84758
85096
  formEditor.destroy();
84759
85097
  };
84760
- }, [additionalModules, editorAdditionalModules, editorProperties, emit, exporterConfig, propertiesPanelConfig, viewerAdditionalModules, viewerProperties]);
85098
+ }, [additionalModules, editorAdditionalModules, editorProperties, editorSlots, emit, exporterConfig, propertiesPanelConfig, viewerAdditionalModules, viewerProperties]);
84761
85099
 
84762
85100
  // initialize data through props
84763
85101
  p(() => {