@rulecms/source-components-angular 0.2.0 → 0.4.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 (60) hide show
  1. package/README.md +1 -0
  2. package/fesm2022/rulecms-source-components-angular.mjs +714 -112
  3. package/fesm2022/rulecms-source-components-angular.mjs.map +1 -1
  4. package/lib/base-components/display/card-entries/card-entries.d.ts.map +1 -1
  5. package/lib/base-components/display/component-attributes/component-name-to-modify-attribute.d.ts.map +1 -1
  6. package/lib/base-components/display/components/r-accordion/accordion-attributes.d.ts +1 -0
  7. package/lib/base-components/display/components/r-accordion/accordion-attributes.d.ts.map +1 -1
  8. package/lib/base-components/display/components/r-accordion/attribute-edit-mode-configs.d.ts.map +1 -1
  9. package/lib/base-components/display/components/r-accordion/default-attribute-values.d.ts.map +1 -1
  10. package/lib/base-components/display/components/r-accordion/r-accordion.component.d.ts +1 -0
  11. package/lib/base-components/display/components/r-accordion/r-accordion.component.d.ts.map +1 -1
  12. package/lib/base-components/display/components/r-list/attribute-edit-mode-configs.d.ts.map +1 -1
  13. package/lib/base-components/display/components/r-list/default-attribute-values.d.ts.map +1 -1
  14. package/lib/base-components/display/components/r-list/list-attributes.d.ts +1 -0
  15. package/lib/base-components/display/components/r-list/list-attributes.d.ts.map +1 -1
  16. package/lib/base-components/display/components/r-list/r-list.component.d.ts +1 -0
  17. package/lib/base-components/display/components/r-list/r-list.component.d.ts.map +1 -1
  18. package/lib/base-components/display/components/r-mount/attribute-edit-mode-configs.d.ts +6 -0
  19. package/lib/base-components/display/components/r-mount/attribute-edit-mode-configs.d.ts.map +1 -0
  20. package/lib/base-components/display/components/r-mount/default-attribute-values.d.ts +8 -0
  21. package/lib/base-components/display/components/r-mount/default-attribute-values.d.ts.map +1 -0
  22. package/lib/base-components/display/components/r-mount/display-card-entry.d.ts +3 -0
  23. package/lib/base-components/display/components/r-mount/display-card-entry.d.ts.map +1 -0
  24. package/lib/base-components/display/components/r-mount/mount-attributes.d.ts +34 -0
  25. package/lib/base-components/display/components/r-mount/mount-attributes.d.ts.map +1 -0
  26. package/lib/base-components/display/components/r-mount/r-mount.component.d.ts +31 -0
  27. package/lib/base-components/display/components/r-mount/r-mount.component.d.ts.map +1 -0
  28. package/lib/base-components/display/components/r-mount/resolve-mount-render.d.ts +26 -0
  29. package/lib/base-components/display/components/r-mount/resolve-mount-render.d.ts.map +1 -0
  30. package/lib/base-components/display/components/r-mount/resolve-no-container.d.ts +7 -0
  31. package/lib/base-components/display/components/r-mount/resolve-no-container.d.ts.map +1 -0
  32. package/lib/base-components/display/components/r-mount/resolve-renders-own-element.d.ts +6 -0
  33. package/lib/base-components/display/components/r-mount/resolve-renders-own-element.d.ts.map +1 -0
  34. package/lib/base-components/display/components/r-text/attribute-edit-mode-configs.d.ts.map +1 -1
  35. package/lib/base-components/display/components/r-text/default-attribute-values.d.ts.map +1 -1
  36. package/lib/base-components/display/components/r-text/r-text.component.d.ts +5 -0
  37. package/lib/base-components/display/components/r-text/r-text.component.d.ts.map +1 -1
  38. package/lib/base-components/editor-preview/card-entries/card-entries.d.ts.map +1 -1
  39. package/lib/component-identifiers.d.ts +1 -1
  40. package/lib/component-identifiers.d.ts.map +1 -1
  41. package/lib/shared/inline-markup/inline-markup.component.d.ts +7 -1
  42. package/lib/shared/inline-markup/inline-markup.component.d.ts.map +1 -1
  43. package/lib/shared/inline-markup/parse-inline-markers.d.ts +9 -3
  44. package/lib/shared/inline-markup/parse-inline-markers.d.ts.map +1 -1
  45. package/lib/shared/inline-markup/serialize-inline-markers.d.ts.map +1 -1
  46. package/lib/shared/span-styles/index.d.ts +8 -0
  47. package/lib/shared/span-styles/index.d.ts.map +1 -0
  48. package/lib/shared/span-styles/parse-style-declarations.d.ts +28 -0
  49. package/lib/shared/span-styles/parse-style-declarations.d.ts.map +1 -0
  50. package/lib/shared/span-styles/resolve-class-names.d.ts +23 -0
  51. package/lib/shared/span-styles/resolve-class-names.d.ts.map +1 -0
  52. package/lib/shared/span-styles/resolve-span-style-attributes.d.ts +26 -0
  53. package/lib/shared/span-styles/resolve-span-style-attributes.d.ts.map +1 -0
  54. package/lib/shared/span-styles/span-style-item.d.ts +43 -0
  55. package/lib/shared/span-styles/span-style-item.d.ts.map +1 -0
  56. package/lib/types.d.ts +3 -0
  57. package/lib/types.d.ts.map +1 -1
  58. package/package.json +2 -2
  59. package/public-api.d.ts +4 -0
  60. package/public-api.d.ts.map +1 -1
@@ -1,10 +1,10 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { Input, Component, ViewChild } from '@angular/core';
3
- import { NgStyle } from '@angular/common';
3
+ import { NgStyle, NgComponentOutlet } from '@angular/common';
4
4
  import * as i1 from '@angular/platform-browser';
5
5
 
6
6
  const DEFAULT_CONTENT_TEXT = 'This is a text block. Click to edit and add your own content.';
7
- const defaultAttributeValues$8 = [
7
+ const defaultAttributeValues$9 = [
8
8
  {
9
9
  name: 'content-text-3-resolutions',
10
10
  defaultValues: {
@@ -19,6 +19,10 @@ const defaultAttributeValues$8 = [
19
19
  name: 'links-3-resolutions',
20
20
  defaultValues: {},
21
21
  },
22
+ {
23
+ name: 'inline-styles-3-resolutions',
24
+ defaultValues: {},
25
+ },
22
26
  ];
23
27
 
24
28
  /**
@@ -351,13 +355,266 @@ function resolveLinkAttributes(item) {
351
355
  }
352
356
 
353
357
  /**
354
- * `{` + optional mark letters + optional index + `|` + text + `}`.
358
+ * Runtime guard for entries arriving through componentAttributes (untyped,
359
+ * editor-authored data).
360
+ *
361
+ * Only object shape is checked, for the same reason as `isLinkItem`: one bad
362
+ * field must cost that field rather than the whole entry, so per-field
363
+ * validation belongs to `resolveSpanStyleAttributes` where each value is turned
364
+ * into something an element can carry.
365
+ */
366
+ function isSpanStyleItem(value) {
367
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
368
+ }
369
+ /**
370
+ * True for a cleared slot. Clearing an entry stores `{}` rather than removing
371
+ * the array element, because marker indexes are 1-based positions: dropping an
372
+ * element would silently repoint every later marker.
373
+ *
374
+ * A `label` alone is a cleared slot. It is the author's note to themselves
375
+ * about which entry this is, and an entry that only has a name applies nothing.
376
+ */
377
+ function isEmptySpanStyleItem(item) {
378
+ if (!isSpanStyleItem(item)) {
379
+ return true;
380
+ }
381
+ return [item.classNames, item.declarations].every((value) => typeof value !== 'string' || value.trim() === '');
382
+ }
383
+ /**
384
+ * The entry a `{.n|…}` marker points at, by 1-based position. Tolerant of bad
385
+ * data on every axis — a non-array list, a fractional or out-of-range index, a
386
+ * hole in the array — because none of it may throw during a render.
387
+ */
388
+ function getSpanStyleItemAt(spanStyles, oneBasedIndex) {
389
+ if (!Array.isArray(spanStyles)) {
390
+ return undefined;
391
+ }
392
+ if (!Number.isInteger(oneBasedIndex) || oneBasedIndex < 1) {
393
+ return undefined;
394
+ }
395
+ const entry = spanStyles[oneBasedIndex - 1];
396
+ return isSpanStyleItem(entry) ? entry : undefined;
397
+ }
398
+
399
+ /**
400
+ * A class name is a *selector* into CSS the customer already ships. This
401
+ * library cannot see those rules, so it cannot validate a name against
402
+ * anything — the only useful question is whether the token could damage the
403
+ * document it lands in, and the answer is almost always no: every renderer sets
404
+ * `class` through a DOM property or a framework binding, never by pasting into
405
+ * markup.
406
+ *
407
+ * So the filter is deliberately permissive. Utility frameworks make class names
408
+ * that look like code — `md:hover:text-lg`, `w-1/2`, `text-[#c00]`,
409
+ * `[&>svg]:size-4` — and a tidier-looking allowlist would reject those and be
410
+ * read as a bug.
411
+ */
412
+ /** Whitespace separates tokens, so it cannot appear inside one. */
413
+ const TOKEN_SEPARATOR = /\s+/;
414
+ /**
415
+ * Control characters, and the two brace characters. Braces are not dangerous in
416
+ * a class attribute; they are rejected because a token containing one is nearly
417
+ * always an inline marker the author pasted into the wrong field, and silently
418
+ * accepting it would style nothing and explain nothing.
419
+ */
420
+ // eslint-disable-next-line no-control-regex -- control characters are what this matches
421
+ const REJECTED_IN_TOKEN = /[\u0000-\u001f\u007f{}]/;
422
+ /**
423
+ * Bounds, so a pathological value cannot become a pathological `class`
424
+ * attribute. Both are far past anything a person types by hand.
425
+ */
426
+ const MAX_TOKENS = 50;
427
+ const MAX_TOKEN_LENGTH = 120;
428
+ const isUsableToken = (token) =>
429
+ // Splitting a blank string yields one empty token, which is not a class name.
430
+ token !== '' && token.length <= MAX_TOKEN_LENGTH && !REJECTED_IN_TOKEN.test(token);
431
+ /**
432
+ * The `class` value for a styling entry, or `undefined` when it has none worth
433
+ * setting.
434
+ *
435
+ * Duplicates collapse and order is the author's. One bad token costs that
436
+ * token: the rest of a mostly-fine list still applies, which is the same
437
+ * per-field tolerance `resolveLinkAttributes` gives a link entry.
438
+ */
439
+ function resolveClassNames(value) {
440
+ if (typeof value !== 'string') {
441
+ return undefined;
442
+ }
443
+ const tokens = value.trim().split(TOKEN_SEPARATOR).filter(isUsableToken);
444
+ if (tokens.length === 0) {
445
+ return undefined;
446
+ }
447
+ return Array.from(new Set(tokens)).slice(0, MAX_TOKENS).join(' ');
448
+ }
449
+
450
+ /**
451
+ * Declarations are split on `;` without tracking quotes, so a value containing
452
+ * a semicolon inside a string — `content: "a;b"` — is cut in half and both
453
+ * halves are then rejected as malformed. That is an acceptable trade: the
454
+ * properties worth setting on a run of words do not take string values, and a
455
+ * quote-aware splitter is a second CSS parser to keep correct.
456
+ */
457
+ const DECLARATION_SEPARATOR = ';';
458
+ /** `--custom-property`, or an ordinary property with an optional vendor prefix. */
459
+ const PROPERTY_NAME = /^(--[a-zA-Z0-9-]+|-?[a-zA-Z][a-zA-Z0-9-]*)$/;
460
+ /**
461
+ * Properties that do something other than style text, whatever their value.
462
+ *
463
+ * `behavior` and `-moz-binding` attach script to an element through CSS in old
464
+ * engines. They are long dead, and they are named here anyway: the cost is one
465
+ * line, and the alternative is relying on every reader of this file knowing
466
+ * that CSS was once executable.
467
+ */
468
+ const REJECTED_PROPERTIES = new Set(['behavior', '-moz-binding']);
469
+ /**
470
+ * Constructs that make a declaration do more than describe appearance.
471
+ *
472
+ * `url(` is rejected rather than filtered because every reason to allow it is
473
+ * better served elsewhere: a background image is a component, and a `url()` in
474
+ * authored text is either a tracking pixel, a `data:` payload, or a
475
+ * `javascript:` scheme an older engine will still run. A backslash is rejected
476
+ * for the same reason in one step: CSS escapes let `\75 rl(` spell `url(`, so
477
+ * allowing escapes would mean decoding them before every other check.
478
+ */
479
+ const REJECTED_IN_VALUE = [
480
+ 'url(',
481
+ 'expression(',
482
+ 'javascript:',
483
+ 'vbscript:',
484
+ '@',
485
+ '\\',
486
+ '<',
487
+ '>',
488
+ '/*',
489
+ '*/',
490
+ ];
491
+ /** Control characters, and the braces that would end a declaration list. */
492
+ // eslint-disable-next-line no-control-regex -- control characters are what this matches
493
+ const REJECTED_CHARACTERS = /[\u0000-\u001f\u007f{}]/;
494
+ /**
495
+ * React drops `!important` from a style object and warns about it, while the
496
+ * string-based ports would honour it — one authored value behaving differently
497
+ * per platform. It comes off everywhere instead, and the class-names field is
498
+ * the supported way to win a specificity fight.
499
+ */
500
+ const IMPORTANT_FLAG = /\s*!\s*important\s*$/i;
501
+ /** Bounds well past anything typed by hand, so a pasted stylesheet cannot land here. */
502
+ const MAX_DECLARATIONS = 40;
503
+ const MAX_VALUE_LENGTH = 400;
504
+ const isUsableProperty = (property) => PROPERTY_NAME.test(property) && !REJECTED_PROPERTIES.has(property.toLowerCase());
505
+ const isUsableValue = (value) => {
506
+ if (value === '' || value.length > MAX_VALUE_LENGTH) {
507
+ return false;
508
+ }
509
+ if (REJECTED_CHARACTERS.test(value)) {
510
+ return false;
511
+ }
512
+ const lowered = value.toLowerCase();
513
+ return !REJECTED_IN_VALUE.some((rejected) => lowered.includes(rejected));
514
+ };
515
+ /**
516
+ * A declaration to the pair it means, or `undefined` when it is malformed or
517
+ * carries something this module will not apply.
518
+ */
519
+ const parseDeclaration = (declaration) => {
520
+ const separator = declaration.indexOf(':');
521
+ if (separator === -1) {
522
+ return undefined;
523
+ }
524
+ // Only the first colon splits: `background: linear-gradient(…)` keeps the rest.
525
+ const property = declaration.slice(0, separator).trim();
526
+ const value = declaration.slice(separator + 1).replace(IMPORTANT_FLAG, '').trim();
527
+ if (!isUsableProperty(property) || !isUsableValue(value)) {
528
+ return undefined;
529
+ }
530
+ return { property, value };
531
+ };
532
+ /**
533
+ * An authored CSS declaration list to the declarations that will be applied.
534
+ *
535
+ * The list is a denylist over values and an allowlist over property names,
536
+ * which is the same shape as `text-safety/validate-text-content.ts` in the
537
+ * composer and for the same reason: a person writing `color: #c00` should never
538
+ * have to think about this function, while the handful of constructs that turn
539
+ * a declaration into a request or a script are named and refused.
540
+ *
541
+ * A rejected declaration costs only itself. `color: red; behavior: url(x)`
542
+ * still sets the colour, because dropping the whole entry over one bad
543
+ * declaration would lose work the author can see is fine.
544
+ */
545
+ function parseStyleDeclarations(value) {
546
+ if (typeof value !== 'string') {
547
+ return [];
548
+ }
549
+ const parsed = [];
550
+ const seen = new Set();
551
+ for (const declaration of value.split(DECLARATION_SEPARATOR)) {
552
+ if (declaration.trim() === '') {
553
+ continue;
554
+ }
555
+ const pair = parseDeclaration(declaration);
556
+ if (pair === undefined) {
557
+ continue;
558
+ }
559
+ // Last one wins, as it would in a real declaration list.
560
+ if (seen.has(pair.property)) {
561
+ parsed[parsed.findIndex((existing) => existing.property === pair.property)] = pair;
562
+ continue;
563
+ }
564
+ seen.add(pair.property);
565
+ parsed.push(pair);
566
+ if (parsed.length === MAX_DECLARATIONS) {
567
+ break;
568
+ }
569
+ }
570
+ return parsed;
571
+ }
572
+ /**
573
+ * Declarations back to a CSS string, for the places that want a `style`
574
+ * attribute rather than an object — which in this port is every one of them.
575
+ */
576
+ function formatStyleDeclarations(declarations) {
577
+ return declarations.map(({ property, value }) => `${property}: ${value}`).join('; ');
578
+ }
579
+
580
+ /**
581
+ * Turns a stored styling entry into element attributes, or `undefined` when the
582
+ * entry cannot produce any — missing, cleared, or holding nothing that survived
583
+ * the checks in `resolveClassNames` and `parseStyleDeclarations`. Callers render
584
+ * the plain text in that case, exactly as they do for a link that resolves to
585
+ * nothing.
586
+ *
587
+ * Knows nothing about text, markers, or any one component: this is the piece
588
+ * every component rendering inline markup shares.
589
+ */
590
+ function resolveSpanStyleAttributes(item) {
591
+ if (!isSpanStyleItem(item)) {
592
+ return undefined;
593
+ }
594
+ const className = resolveClassNames(item.classNames);
595
+ const declarations = parseStyleDeclarations(item.declarations);
596
+ if (className === undefined && declarations.length === 0) {
597
+ return undefined;
598
+ }
599
+ return {
600
+ ...(className !== undefined ? { className } : {}),
601
+ ...(declarations.length > 0 ? { style: formatStyleDeclarations(declarations) } : {}),
602
+ };
603
+ }
604
+
605
+ /**
606
+ * `{` + optional mark letters + optional link index + optional `.` and styling
607
+ * index + `|` + text + `}`.
355
608
  *
356
609
  * Sticky, so it can be tried at one position without slicing the string. The
357
610
  * text is everything up to the first `}`, which is what makes markers
358
611
  * non-nesting.
612
+ *
613
+ * The dot is what tells the two indexes apart, and it reads as the CSS class
614
+ * selector it usually stands for. It cannot be confused with a link index,
615
+ * because a link index is bare digits and always comes first.
359
616
  */
360
- const MARKER = /\{([a-z]*)(\d*)\|([^}]*)\}/y;
617
+ const MARKER = /\{([a-z]*)(\d*)(?:\.(\d+))?\|([^}]*)\}/y;
361
618
  /**
362
619
  * One letter per mark. Order is not significant (`{bi1|x}` and `{ib1|x}` are
363
620
  * the same span) and repeats are harmless, because the letters resolve to a
@@ -399,9 +656,9 @@ const resolveMarks = (letters) => {
399
656
  * The grammar, and everything it deliberately leaves as literal text, is
400
657
  * specified in `rulecms/__docs__/__ARCHITECTURE__/INLINE_MARKUP_ARCHITECTURE.md`.
401
658
  * In short: a marker is `{`, then lowercase mark letters, then a link index,
402
- * then `|`, then its text, then `}`. At least one of the letters and the index
403
- * must be present, and everything that does not match that shape stays
404
- * verbatim — `{name}`, `{99}`, a lone `{`, JSON fragments, an unterminated
659
+ * then `.` and a styling index, then `|`, then its text, then `}`. At least one
660
+ * of those three must be present, and everything that does not match that shape
661
+ * stays verbatim — `{name}`, `{99}`, a lone `{`, JSON fragments, an unterminated
405
662
  * marker, an unknown mark letter, and empty span text.
406
663
  *
407
664
  * Indexes are 1-based and normalize through a base-10 parse, so `{01|x}` and
@@ -429,7 +686,7 @@ function parseInlineMarkers(value) {
429
686
  }
430
687
  MARKER.lastIndex = open;
431
688
  const match = MARKER.exec(value);
432
- const run = match ? resolveRun(match[1], match[2], match[3]) : undefined;
689
+ const run = match ? resolveRun(match[1], match[2], match[3], match[4]) : undefined;
433
690
  if (run === undefined) {
434
691
  // Not a marker: keep the brace as text and look for the next one.
435
692
  pendingText += value.slice(cursor, open + 1);
@@ -447,13 +704,19 @@ function parseInlineMarkers(value) {
447
704
  /**
448
705
  * One matched `{…|…}` to the run it means, or `undefined` when the match is
449
706
  * not a marker after all and its opening brace is literal text.
707
+ *
708
+ * `styleDigits` is `undefined` when the marker carried no dot at all, which is
709
+ * different from a dot with nothing after it — the regex does not match that,
710
+ * so it never reaches here.
450
711
  */
451
- const resolveRun = (letters, digits, text) => {
712
+ const resolveRun = (letters, digits, styleDigits, text) => {
452
713
  if (letters === BREAK_TOKEN) {
453
714
  // A break carries nothing. `{br|x}` is a mistake, not a break with text.
454
- return digits === '' && text === '' ? { kind: 'break' } : undefined;
715
+ return digits === '' && styleDigits === undefined && text === ''
716
+ ? { kind: 'break' }
717
+ : undefined;
455
718
  }
456
- if (letters === '' && digits === '') {
719
+ if (letters === '' && digits === '' && styleDigits === undefined) {
457
720
  return undefined;
458
721
  }
459
722
  // Empty text has no words to mark or link, and staying literal is what shows
@@ -469,6 +732,7 @@ const resolveRun = (letters, digits, text) => {
469
732
  kind: 'span',
470
733
  text,
471
734
  ...(digits === '' ? {} : { index: Number.parseInt(digits, 10) }),
735
+ ...(styleDigits === undefined ? {} : { styleIndex: Number.parseInt(styleDigits, 10) }),
472
736
  marks,
473
737
  };
474
738
  };
@@ -492,8 +756,14 @@ class InlineMarkupComponent {
492
756
  const anchors = runs.map((run) => run.kind === 'span' && run.index !== undefined
493
757
  ? resolveLinkAttributes(getLinkItemAt(this.links, run.index))
494
758
  : undefined);
759
+ const spanStyles = runs.map((run) => run.kind === 'span' && run.styleIndex !== undefined
760
+ ? resolveSpanStyleAttributes(getSpanStyleItemAt(this.spanStyles, run.styleIndex))
761
+ : undefined);
495
762
  const needsElement = runs.some((run, position) => run.kind === 'break' ||
496
- (run.kind === 'span' && (anchors[position] !== undefined || hasMark(run.marks))));
763
+ (run.kind === 'span' &&
764
+ (anchors[position] !== undefined ||
765
+ spanStyles[position] !== undefined ||
766
+ hasMark(run.marks))));
497
767
  if (!needsElement) {
498
768
  this.renderer.appendChild(this.host.nativeElement, this.renderer.createText(runs.map(plainTextOf).join('')));
499
769
  return;
@@ -507,10 +777,15 @@ class InlineMarkupComponent {
507
777
  this.renderer.appendChild(this.host.nativeElement, this.renderer.createElement('br'));
508
778
  return;
509
779
  }
510
- this.renderer.appendChild(this.host.nativeElement, this.renderSpan(run, anchors[position]));
780
+ this.renderer.appendChild(this.host.nativeElement, this.renderSpan(run, anchors[position], spanStyles[position]));
511
781
  });
512
782
  }
513
- renderSpan(run, anchor) {
783
+ /**
784
+ * The anchor stays innermost and a styling entry becomes the outermost
785
+ * element: the marks say what these words *are*, and the styling says how
786
+ * this run of words looks, which is the container of the other two.
787
+ */
788
+ renderSpan(run, anchor, spanStyle) {
514
789
  let node = anchor === undefined
515
790
  ? this.renderer.createText(run.text)
516
791
  : this.createAnchor(anchor, run.text);
@@ -522,7 +797,16 @@ class InlineMarkupComponent {
522
797
  node = wrapper;
523
798
  }
524
799
  }
525
- return node;
800
+ if (spanStyle === undefined) {
801
+ return node;
802
+ }
803
+ const styled = this.renderer.createElement('span');
804
+ if (spanStyle.className)
805
+ this.renderer.setAttribute(styled, 'class', spanStyle.className);
806
+ if (spanStyle.style)
807
+ this.renderer.setAttribute(styled, 'style', spanStyle.style);
808
+ this.renderer.appendChild(styled, node);
809
+ return styled;
526
810
  }
527
811
  createAnchor(anchor, text) {
528
812
  const el = this.renderer.createElement('a');
@@ -553,7 +837,7 @@ class InlineMarkupComponent {
553
837
  }
554
838
  }
555
839
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: InlineMarkupComponent, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component }); }
556
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: InlineMarkupComponent, isStandalone: true, selector: "rulecms-inline-markup", inputs: { text: "text", links: "links" }, host: { styleAttribute: "display: contents" }, usesOnChanges: true, ngImport: i0, template: '', isInline: true }); }
840
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: InlineMarkupComponent, isStandalone: true, selector: "rulecms-inline-markup", inputs: { text: "text", links: "links", spanStyles: "spanStyles" }, host: { styleAttribute: "display: contents" }, usesOnChanges: true, ngImport: i0, template: '', isInline: true }); }
557
841
  }
558
842
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: InlineMarkupComponent, decorators: [{
559
843
  type: Component,
@@ -567,6 +851,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
567
851
  type: Input
568
852
  }], links: [{
569
853
  type: Input
854
+ }], spanStyles: [{
855
+ type: Input
570
856
  }] } });
571
857
 
572
858
  class HtmlTagComponent {
@@ -646,6 +932,13 @@ class RTextComponent {
646
932
  get links() {
647
933
  return this.componentAttributes?.['links-3-resolutions'];
648
934
  }
935
+ /**
936
+ * The two index markers — `{n|…}` into the link list and `{.n|…}` into the
937
+ * styling list — resolve by 1-based position against separate attributes.
938
+ */
939
+ get spanStyles() {
940
+ return this.componentAttributes?.['inline-styles-3-resolutions'];
941
+ }
649
942
  get tag() {
650
943
  return resolveEnclosingTag(this.componentAttributes);
651
944
  }
@@ -653,12 +946,12 @@ class RTextComponent {
653
946
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.25", type: RTextComponent, isStandalone: true, selector: "rulecms-r-text", inputs: { componentAttributes: "componentAttributes", styleProps: "styleProps", className: "className" }, host: { styleAttribute: "display: contents" }, ngImport: i0, template: `
654
947
  @if (tag) {
655
948
  <rulecms-html-tag [tag]="tag" [styleProps]="styleProps" [className]="className">
656
- <rulecms-inline-markup [text]="content" [links]="links" />
949
+ <rulecms-inline-markup [text]="content" [links]="links" [spanStyles]="spanStyles" />
657
950
  </rulecms-html-tag>
658
951
  } @else {
659
- <rulecms-inline-markup [text]="content" [links]="links" />
952
+ <rulecms-inline-markup [text]="content" [links]="links" [spanStyles]="spanStyles" />
660
953
  }
661
- `, isInline: true, dependencies: [{ kind: "component", type: InlineMarkupComponent, selector: "rulecms-inline-markup", inputs: ["text", "links"] }, { kind: "component", type: HtmlTagComponent, selector: "rulecms-html-tag", inputs: ["tag", "styleProps", "className"] }] }); }
954
+ `, isInline: true, dependencies: [{ kind: "component", type: InlineMarkupComponent, selector: "rulecms-inline-markup", inputs: ["text", "links", "spanStyles"] }, { kind: "component", type: HtmlTagComponent, selector: "rulecms-html-tag", inputs: ["tag", "styleProps", "className"] }] }); }
662
955
  }
663
956
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: RTextComponent, decorators: [{
664
957
  type: Component,
@@ -669,10 +962,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
669
962
  template: `
670
963
  @if (tag) {
671
964
  <rulecms-html-tag [tag]="tag" [styleProps]="styleProps" [className]="className">
672
- <rulecms-inline-markup [text]="content" [links]="links" />
965
+ <rulecms-inline-markup [text]="content" [links]="links" [spanStyles]="spanStyles" />
673
966
  </rulecms-html-tag>
674
967
  } @else {
675
- <rulecms-inline-markup [text]="content" [links]="links" />
968
+ <rulecms-inline-markup [text]="content" [links]="links" [spanStyles]="spanStyles" />
676
969
  }
677
970
  `,
678
971
  host: { style: 'display: contents' },
@@ -695,7 +988,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
695
988
  * - Block tag (`p`, `div`, `h1`, `section`, …): RText puts styleProps on the
696
989
  * tag itself → no wrapper, one less element in the DOM.
697
990
  */
698
- const resolveNoContainer$8 = ({ componentAttributes, }) => !isBlockEnclosingTag(resolveEnclosingTag(componentAttributes));
991
+ const resolveNoContainer$9 = ({ componentAttributes, }) => !isBlockEnclosingTag(resolveEnclosingTag(componentAttributes));
699
992
 
700
993
  /**
701
994
  * True when RText is about to render an element that can carry a className —
@@ -705,9 +998,9 @@ const resolveNoContainer$8 = ({ componentAttributes, }) => !isBlockEnclosingTag(
705
998
  * disagreed with what the component renders, a class would either land on
706
999
  * nothing or force a redundant wrapper.
707
1000
  */
708
- const resolveRendersOwnElement$8 = ({ componentAttributes, }) => !!resolveEnclosingTag(componentAttributes);
1001
+ const resolveRendersOwnElement$9 = ({ componentAttributes, }) => !!resolveEnclosingTag(componentAttributes);
709
1002
 
710
- const attributeEditModeConfigs$8 = [
1003
+ const attributeEditModeConfigs$9 = [
711
1004
  {
712
1005
  name: 'content-text-3-resolutions',
713
1006
  defaultValues: {},
@@ -724,6 +1017,12 @@ const attributeEditModeConfigs$8 = [
724
1017
  subHeading: 'Links this text can point at. To link part of the text, wrap those words in {1|linked text}, where the number is the position of the entry in this list counting from 1; clearing an entry leaves its position in place so the other numbers keep pointing where they did, for 3 different device resolutions',
725
1018
  defaultValues: {},
726
1019
  },
1020
+ {
1021
+ name: 'inline-styles-3-resolutions',
1022
+ heading: 'Class Names & Styles',
1023
+ subHeading: 'Class names and CSS this text can apply to part of itself, e.g. to give a few words their own colour. To apply an entry, wrap those words in {.1|styled text}, where the number is the position of the entry in this list counting from 1; clearing an entry leaves its position in place so the other numbers keep pointing where they did, for 3 different device resolutions',
1024
+ defaultValues: {},
1025
+ },
727
1026
  ];
728
1027
 
729
1028
  const ComponentIdentifiers = {
@@ -737,15 +1036,16 @@ const ComponentIdentifiers = {
737
1036
  rEmbed: 'r-embed',
738
1037
  rList: 'r-list',
739
1038
  rAccordion: 'r-accordion',
1039
+ rMount: 'r-mount',
740
1040
  };
741
1041
 
742
- const displayCardEntry$8 = {
1042
+ const displayCardEntry$9 = {
743
1043
  id: ComponentIdentifiers.rText,
744
- resolveNoContainer: resolveNoContainer$8,
745
- resolveRendersOwnElement: resolveRendersOwnElement$8,
1044
+ resolveNoContainer: resolveNoContainer$9,
1045
+ resolveRendersOwnElement: resolveRendersOwnElement$9,
746
1046
  card: RTextComponent,
747
- defaultAttributeValues: defaultAttributeValues$8,
748
- attributeEditModeConfigs: attributeEditModeConfigs$8,
1047
+ defaultAttributeValues: defaultAttributeValues$9,
1048
+ attributeEditModeConfigs: attributeEditModeConfigs$9,
749
1049
  };
750
1050
 
751
1051
  // Builds Cloudinary delivery URLs directly so the component renders a complete
@@ -930,15 +1230,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
930
1230
  * only add a redundant element. There is no configuration that changes this,
931
1231
  * so the answer is the same for every render.
932
1232
  */
933
- const resolveNoContainer$7 = () => false;
1233
+ const resolveNoContainer$8 = () => false;
934
1234
 
935
1235
  /**
936
1236
  * Always renders an `<img>`, which can carry a className. There is no
937
1237
  * configuration that changes this.
938
1238
  */
939
- const resolveRendersOwnElement$7 = () => true;
1239
+ const resolveRendersOwnElement$8 = () => true;
940
1240
 
941
- const defaultAttributeValues$7 = [
1241
+ const defaultAttributeValues$8 = [
942
1242
  {
943
1243
  name: 'cloudinary-cloud-name',
944
1244
  defaultValues: {},
@@ -983,7 +1283,7 @@ const defaultAttributeValues$7 = [
983
1283
  },
984
1284
  ];
985
1285
 
986
- const attributeEditModeConfigs$7 = [
1286
+ const attributeEditModeConfigs$8 = [
987
1287
  {
988
1288
  name: 'cloudinary-cloud-name',
989
1289
  heading: 'Cloudinary Cloud Name',
@@ -1050,13 +1350,13 @@ const attributeEditModeConfigs$7 = [
1050
1350
  },
1051
1351
  ];
1052
1352
 
1053
- const displayCardEntry$7 = {
1353
+ const displayCardEntry$8 = {
1054
1354
  id: ComponentIdentifiers.cloudinaryAdvancedImage,
1055
- resolveNoContainer: resolveNoContainer$7,
1056
- resolveRendersOwnElement: resolveRendersOwnElement$7,
1355
+ resolveNoContainer: resolveNoContainer$8,
1356
+ resolveRendersOwnElement: resolveRendersOwnElement$8,
1057
1357
  card: CloudinaryAdvancedImageComponent,
1058
- defaultAttributeValues: defaultAttributeValues$7,
1059
- attributeEditModeConfigs: attributeEditModeConfigs$7,
1358
+ defaultAttributeValues: defaultAttributeValues$8,
1359
+ attributeEditModeConfigs: attributeEditModeConfigs$8,
1060
1360
  };
1061
1361
 
1062
1362
  // Builds media-host delivery URLs directly so the component renders a complete
@@ -1271,15 +1571,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
1271
1571
  * only add a redundant element. There is no configuration that changes this,
1272
1572
  * so the answer is the same for every render.
1273
1573
  */
1274
- const resolveNoContainer$6 = () => false;
1574
+ const resolveNoContainer$7 = () => false;
1275
1575
 
1276
1576
  /**
1277
1577
  * Always renders a `<video>`, which can carry a className. There is no
1278
1578
  * configuration that changes this.
1279
1579
  */
1280
- const resolveRendersOwnElement$6 = () => true;
1580
+ const resolveRendersOwnElement$7 = () => true;
1281
1581
 
1282
- const defaultAttributeValues$6 = [
1582
+ const defaultAttributeValues$7 = [
1283
1583
  {
1284
1584
  name: 'video-host-cloud-name',
1285
1585
  defaultValues: {},
@@ -1336,7 +1636,7 @@ const defaultAttributeValues$6 = [
1336
1636
  },
1337
1637
  ];
1338
1638
 
1339
- const attributeEditModeConfigs$6 = [
1639
+ const attributeEditModeConfigs$7 = [
1340
1640
  {
1341
1641
  name: 'video-host-cloud-name',
1342
1642
  heading: 'Video Host Cloud Name',
@@ -1421,13 +1721,13 @@ const attributeEditModeConfigs$6 = [
1421
1721
  },
1422
1722
  ];
1423
1723
 
1424
- const displayCardEntry$6 = {
1724
+ const displayCardEntry$7 = {
1425
1725
  id: ComponentIdentifiers.rVideo,
1426
- resolveNoContainer: resolveNoContainer$6,
1427
- resolveRendersOwnElement: resolveRendersOwnElement$6,
1726
+ resolveNoContainer: resolveNoContainer$7,
1727
+ resolveRendersOwnElement: resolveRendersOwnElement$7,
1428
1728
  card: RVideoComponent,
1429
- defaultAttributeValues: defaultAttributeValues$6,
1430
- attributeEditModeConfigs: attributeEditModeConfigs$6,
1729
+ defaultAttributeValues: defaultAttributeValues$7,
1730
+ attributeEditModeConfigs: attributeEditModeConfigs$7,
1431
1731
  };
1432
1732
 
1433
1733
  /**
@@ -1635,16 +1935,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
1635
1935
  * and colour (via currentColor) directly, so a wrapper div would only add a
1636
1936
  * redundant element. No configuration changes that.
1637
1937
  */
1638
- const resolveNoContainer$5 = () => false;
1938
+ const resolveNoContainer$6 = () => false;
1639
1939
 
1640
1940
  /**
1641
1941
  * The `<svg>` carries the className. An unresolvable attribute renders no
1642
1942
  * element at all, but that is a data error rather than a different root type,
1643
1943
  * so the configured happy path is what this answers for.
1644
1944
  */
1645
- const resolveRendersOwnElement$5 = () => true;
1945
+ const resolveRendersOwnElement$6 = () => true;
1646
1946
 
1647
- const defaultAttributeValues$5 = [
1947
+ const defaultAttributeValues$6 = [
1648
1948
  {
1649
1949
  // Holds `{ name, iconNode }`, not a bare name: the composer stamps the
1650
1950
  // geometry when the author picks the icon so the render path never needs
@@ -1656,7 +1956,7 @@ const defaultAttributeValues$5 = [
1656
1956
  },
1657
1957
  ];
1658
1958
 
1659
- const attributeEditModeConfigs$5 = [
1959
+ const attributeEditModeConfigs$6 = [
1660
1960
  {
1661
1961
  name: 'icon-name-3-resolutions',
1662
1962
  heading: 'Icon',
@@ -1667,13 +1967,13 @@ const attributeEditModeConfigs$5 = [
1667
1967
  },
1668
1968
  ];
1669
1969
 
1670
- const displayCardEntry$5 = {
1970
+ const displayCardEntry$6 = {
1671
1971
  id: ComponentIdentifiers.rIcon,
1672
- resolveNoContainer: resolveNoContainer$5,
1673
- resolveRendersOwnElement: resolveRendersOwnElement$5,
1972
+ resolveNoContainer: resolveNoContainer$6,
1973
+ resolveRendersOwnElement: resolveRendersOwnElement$6,
1674
1974
  card: RIconComponent,
1675
- defaultAttributeValues: defaultAttributeValues$5,
1676
- attributeEditModeConfigs: attributeEditModeConfigs$5,
1975
+ defaultAttributeValues: defaultAttributeValues$6,
1976
+ attributeEditModeConfigs: attributeEditModeConfigs$6,
1677
1977
  };
1678
1978
 
1679
1979
  /**
@@ -2400,13 +2700,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
2400
2700
  * nothing, and would put the column's background behind the button rather than
2401
2701
  * on it. No configuration changes that.
2402
2702
  */
2403
- const resolveNoContainer$4 = () => false;
2703
+ const resolveNoContainer$5 = () => false;
2404
2704
 
2405
2705
  /**
2406
2706
  * There is always a root element to carry the class: the button is the
2407
2707
  * component, so no configuration leaves it without one.
2408
2708
  */
2409
- const resolveRendersOwnElement$4 = () => true;
2709
+ const resolveRendersOwnElement$5 = () => true;
2410
2710
 
2411
2711
  /**
2412
2712
  * Seeds the button's own semantics only.
@@ -2419,7 +2719,7 @@ const resolveRendersOwnElement$4 = () => true;
2419
2719
  * happened to cover. Leaving the bag empty is what lets "unset means preset"
2420
2720
  * be true.
2421
2721
  */
2422
- const defaultAttributeValues$4 = [
2722
+ const defaultAttributeValues$5 = [
2423
2723
  {
2424
2724
  name: BUTTON_PRESET_ATTRIBUTE,
2425
2725
  defaultValues: {
@@ -2443,7 +2743,7 @@ const defaultAttributeValues$4 = [
2443
2743
  * documentation registry asserts its pages match that sequence, so moving an
2444
2744
  * entry here moves it in both places at once.
2445
2745
  */
2446
- const attributeEditModeConfigs$4 = [
2746
+ const attributeEditModeConfigs$5 = [
2447
2747
  {
2448
2748
  name: BUTTON_PRESET_ATTRIBUTE,
2449
2749
  heading: 'Button Style',
@@ -2482,13 +2782,13 @@ const attributeEditModeConfigs$4 = [
2482
2782
  },
2483
2783
  ];
2484
2784
 
2485
- const displayCardEntry$4 = {
2785
+ const displayCardEntry$5 = {
2486
2786
  id: ComponentIdentifiers.rButton,
2487
- resolveNoContainer: resolveNoContainer$4,
2488
- resolveRendersOwnElement: resolveRendersOwnElement$4,
2787
+ resolveNoContainer: resolveNoContainer$5,
2788
+ resolveRendersOwnElement: resolveRendersOwnElement$5,
2489
2789
  card: RButtonComponent,
2490
- defaultAttributeValues: defaultAttributeValues$4,
2491
- attributeEditModeConfigs: attributeEditModeConfigs$4,
2790
+ defaultAttributeValues: defaultAttributeValues$5,
2791
+ attributeEditModeConfigs: attributeEditModeConfigs$5,
2492
2792
  };
2493
2793
 
2494
2794
  /**
@@ -2618,13 +2918,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
2618
2918
  * width and border on itself, so a wrapper div would only add a redundant
2619
2919
  * element. No configuration changes that.
2620
2920
  */
2621
- const resolveNoContainer$3 = () => false;
2921
+ const resolveNoContainer$4 = () => false;
2622
2922
 
2623
2923
  /**
2624
2924
  * The `<hr>` carries the className. There is always exactly one element and it
2625
2925
  * is always the root, so there is nothing for this to depend on.
2626
2926
  */
2627
- const resolveRendersOwnElement$3 = () => true;
2927
+ const resolveRendersOwnElement$4 = () => true;
2628
2928
 
2629
2929
  /**
2630
2930
  * Seeds the divider's own two settings so the drawer opens on the values a
@@ -2634,7 +2934,7 @@ const resolveRendersOwnElement$3 = () => true;
2634
2934
  * names are the divider's own, so nothing in Column Styles writes to them and
2635
2935
  * a seeded value can never shadow a style the author set.
2636
2936
  */
2637
- const defaultAttributeValues$3 = [
2937
+ const defaultAttributeValues$4 = [
2638
2938
  {
2639
2939
  name: DIVIDER_STYLE_ATTRIBUTE,
2640
2940
  defaultValues: {
@@ -2656,7 +2956,7 @@ const defaultAttributeValues$3 = [
2656
2956
  * documentation registry asserts its pages match that sequence, so moving an
2657
2957
  * entry here moves it in both places at once.
2658
2958
  */
2659
- const attributeEditModeConfigs$3 = [
2959
+ const attributeEditModeConfigs$4 = [
2660
2960
  {
2661
2961
  name: DIVIDER_STYLE_ATTRIBUTE,
2662
2962
  heading: 'Line Style',
@@ -2671,13 +2971,13 @@ const attributeEditModeConfigs$3 = [
2671
2971
  },
2672
2972
  ];
2673
2973
 
2674
- const displayCardEntry$3 = {
2974
+ const displayCardEntry$4 = {
2675
2975
  id: ComponentIdentifiers.rDivider,
2676
- resolveNoContainer: resolveNoContainer$3,
2677
- resolveRendersOwnElement: resolveRendersOwnElement$3,
2976
+ resolveNoContainer: resolveNoContainer$4,
2977
+ resolveRendersOwnElement: resolveRendersOwnElement$4,
2678
2978
  card: RDividerComponent,
2679
- defaultAttributeValues: defaultAttributeValues$3,
2680
- attributeEditModeConfigs: attributeEditModeConfigs$3,
2979
+ defaultAttributeValues: defaultAttributeValues$4,
2980
+ attributeEditModeConfigs: attributeEditModeConfigs$4,
2681
2981
  };
2682
2982
 
2683
2983
  /**
@@ -3115,7 +3415,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
3115
3415
  * Deciding this from `resolveEmbed`, rather than from the fact that this
3116
3416
  * component "is an iframe component", is what keeps the two in step.
3117
3417
  */
3118
- const resolveNoContainer$2 = ({ componentAttributes }) => resolveEmbed(componentAttributes) === null;
3418
+ const resolveNoContainer$3 = ({ componentAttributes }) => resolveEmbed(componentAttributes) === null;
3119
3419
 
3120
3420
  /**
3121
3421
  * The iframe carries the className — when there is one. A rejected or unset
@@ -3123,7 +3423,7 @@ const resolveNoContainer$2 = ({ componentAttributes }) => resolveEmbed(component
3123
3423
  * disappears without trace, so this answers from the same resolution the
3124
3424
  * component uses.
3125
3425
  */
3126
- const resolveRendersOwnElement$2 = ({ componentAttributes, }) => resolveEmbed(componentAttributes) !== null;
3426
+ const resolveRendersOwnElement$3 = ({ componentAttributes, }) => resolveEmbed(componentAttributes) !== null;
3127
3427
 
3128
3428
  /**
3129
3429
  * Nothing is seeded, including the URL.
@@ -3136,7 +3436,7 @@ const resolveRendersOwnElement$2 = ({ componentAttributes, }) => resolveEmbed(co
3136
3436
  * Aspect ratio is left unset on purpose too, so it resolves from the provider
3137
3437
  * — a map is 4:3 and a video is 16:9 without either being written down here.
3138
3438
  */
3139
- const defaultAttributeValues$2 = [
3439
+ const defaultAttributeValues$3 = [
3140
3440
  {
3141
3441
  name: 'embed-url-3-resolutions',
3142
3442
  defaultValues: {},
@@ -3160,7 +3460,7 @@ const defaultAttributeValues$2 = [
3160
3460
  * the URL first because nothing renders without it, then the three settings
3161
3461
  * that only matter once something does.
3162
3462
  */
3163
- const attributeEditModeConfigs$2 = [
3463
+ const attributeEditModeConfigs$3 = [
3164
3464
  {
3165
3465
  name: 'embed-url-3-resolutions',
3166
3466
  heading: 'Embed URL',
@@ -3187,13 +3487,13 @@ const attributeEditModeConfigs$2 = [
3187
3487
  },
3188
3488
  ];
3189
3489
 
3190
- const displayCardEntry$2 = {
3490
+ const displayCardEntry$3 = {
3191
3491
  id: ComponentIdentifiers.rEmbed,
3192
- resolveNoContainer: resolveNoContainer$2,
3193
- resolveRendersOwnElement: resolveRendersOwnElement$2,
3492
+ resolveNoContainer: resolveNoContainer$3,
3493
+ resolveRendersOwnElement: resolveRendersOwnElement$3,
3194
3494
  card: REmbedComponent,
3195
- defaultAttributeValues: defaultAttributeValues$2,
3196
- attributeEditModeConfigs: attributeEditModeConfigs$2,
3495
+ defaultAttributeValues: defaultAttributeValues$3,
3496
+ attributeEditModeConfigs: attributeEditModeConfigs$3,
3197
3497
  };
3198
3498
 
3199
3499
  /**
@@ -3222,6 +3522,7 @@ const LIST_MARKER_ATTRIBUTE = 'list-marker-3-resolutions';
3222
3522
  const LIST_START_ATTRIBUTE = 'list-start-3-resolutions';
3223
3523
  const LIST_REVERSED_ATTRIBUTE = 'list-reversed-3-resolutions';
3224
3524
  const LIST_LINKS_ATTRIBUTE = 'list-links-3-resolutions';
3525
+ const LIST_INLINE_STYLES_ATTRIBUTE = 'list-inline-styles-3-resolutions';
3225
3526
  /** What a freshly dropped list shows before the author types anything. */
3226
3527
  const DEFAULT_LIST_ITEMS = 'First item\nSecond item\nThird item';
3227
3528
  const LIST_KINDS = ['unordered', 'ordered'];
@@ -3314,6 +3615,9 @@ class RListComponent {
3314
3615
  get links() {
3315
3616
  return this.componentAttributes?.[LIST_LINKS_ATTRIBUTE];
3316
3617
  }
3618
+ get spanStyles() {
3619
+ return this.componentAttributes?.[LIST_INLINE_STYLES_ATTRIBUTE];
3620
+ }
3317
3621
  get style() {
3318
3622
  return {
3319
3623
  ...(this.marker !== undefined ? { listStyleType: this.marker } : {}),
@@ -3338,7 +3642,7 @@ class RListComponent {
3338
3642
  <ol [ngStyle]="style" [class]="className || undefined" [attr.start]="start" [attr.reversed]="reversed ? true : null">
3339
3643
  @for (item of items; track $index) {
3340
3644
  <li>
3341
- <rulecms-inline-markup [text]="item" [links]="links" />
3645
+ <rulecms-inline-markup [text]="item" [links]="links" [spanStyles]="spanStyles" />
3342
3646
  </li>
3343
3647
  }
3344
3648
  </ol>
@@ -3346,12 +3650,12 @@ class RListComponent {
3346
3650
  <ul [ngStyle]="style" [class]="className || undefined">
3347
3651
  @for (item of items; track $index) {
3348
3652
  <li>
3349
- <rulecms-inline-markup [text]="item" [links]="links" />
3653
+ <rulecms-inline-markup [text]="item" [links]="links" [spanStyles]="spanStyles" />
3350
3654
  </li>
3351
3655
  }
3352
3656
  </ul>
3353
3657
  }
3354
- `, isInline: true, dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: InlineMarkupComponent, selector: "rulecms-inline-markup", inputs: ["text", "links"] }] }); }
3658
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: InlineMarkupComponent, selector: "rulecms-inline-markup", inputs: ["text", "links", "spanStyles"] }] }); }
3355
3659
  }
3356
3660
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: RListComponent, decorators: [{
3357
3661
  type: Component,
@@ -3364,7 +3668,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
3364
3668
  <ol [ngStyle]="style" [class]="className || undefined" [attr.start]="start" [attr.reversed]="reversed ? true : null">
3365
3669
  @for (item of items; track $index) {
3366
3670
  <li>
3367
- <rulecms-inline-markup [text]="item" [links]="links" />
3671
+ <rulecms-inline-markup [text]="item" [links]="links" [spanStyles]="spanStyles" />
3368
3672
  </li>
3369
3673
  }
3370
3674
  </ol>
@@ -3372,7 +3676,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
3372
3676
  <ul [ngStyle]="style" [class]="className || undefined">
3373
3677
  @for (item of items; track $index) {
3374
3678
  <li>
3375
- <rulecms-inline-markup [text]="item" [links]="links" />
3679
+ <rulecms-inline-markup [text]="item" [links]="links" [spanStyles]="spanStyles" />
3376
3680
  </li>
3377
3681
  }
3378
3682
  </ul>
@@ -3394,16 +3698,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
3394
3698
  * that element itself. No configuration changes the answer, so no wrapper
3395
3699
  * div, ever.
3396
3700
  */
3397
- const resolveNoContainer$1 = () => false;
3701
+ const resolveNoContainer$2 = () => false;
3398
3702
 
3399
3703
  /**
3400
3704
  * RList's root is always a real element — `<ul>` or `<ol>` — so it can always
3401
3705
  * carry the column's className, and the component applies the one it
3402
3706
  * receives.
3403
3707
  */
3404
- const resolveRendersOwnElement$1 = () => true;
3708
+ const resolveRendersOwnElement$2 = () => true;
3405
3709
 
3406
- const defaultAttributeValues$1 = [
3710
+ const defaultAttributeValues$2 = [
3407
3711
  {
3408
3712
  name: LIST_ITEMS_ATTRIBUTE,
3409
3713
  defaultValues: {
@@ -3435,6 +3739,10 @@ const defaultAttributeValues$1 = [
3435
3739
  name: LIST_LINKS_ATTRIBUTE,
3436
3740
  defaultValues: {},
3437
3741
  },
3742
+ {
3743
+ name: LIST_INLINE_STYLES_ATTRIBUTE,
3744
+ defaultValues: {},
3745
+ },
3438
3746
  ];
3439
3747
 
3440
3748
  /**
@@ -3444,7 +3752,7 @@ const defaultAttributeValues$1 = [
3444
3752
  * documentation registry asserts its pages match that sequence, so moving an
3445
3753
  * entry here moves it in both places at once.
3446
3754
  */
3447
- const attributeEditModeConfigs$1 = [
3755
+ const attributeEditModeConfigs$2 = [
3448
3756
  {
3449
3757
  name: LIST_ITEMS_ATTRIBUTE,
3450
3758
  heading: 'List Items',
@@ -3481,15 +3789,21 @@ const attributeEditModeConfigs$1 = [
3481
3789
  subHeading: 'Links the items can point at. To link part of an item, wrap those words in {1|linked text}, where the number is the position of the entry in this list counting from 1; clearing an entry leaves its position in place so the other numbers keep pointing where they did, for 3 different device resolutions',
3482
3790
  defaultValues: {},
3483
3791
  },
3792
+ {
3793
+ name: LIST_INLINE_STYLES_ATTRIBUTE,
3794
+ heading: 'Class Names & Styles',
3795
+ subHeading: 'Class names and CSS the items can apply to part of themselves, e.g. to give a few words their own colour. To apply an entry, wrap those words in {.1|styled text}, where the number is the position of the entry in this list counting from 1; clearing an entry leaves its position in place so the other numbers keep pointing where they did, for 3 different device resolutions',
3796
+ defaultValues: {},
3797
+ },
3484
3798
  ];
3485
3799
 
3486
- const displayCardEntry$1 = {
3800
+ const displayCardEntry$2 = {
3487
3801
  id: ComponentIdentifiers.rList,
3488
- resolveNoContainer: resolveNoContainer$1,
3489
- resolveRendersOwnElement: resolveRendersOwnElement$1,
3802
+ resolveNoContainer: resolveNoContainer$2,
3803
+ resolveRendersOwnElement: resolveRendersOwnElement$2,
3490
3804
  card: RListComponent,
3491
- defaultAttributeValues: defaultAttributeValues$1,
3492
- attributeEditModeConfigs: attributeEditModeConfigs$1,
3805
+ defaultAttributeValues: defaultAttributeValues$2,
3806
+ attributeEditModeConfigs: attributeEditModeConfigs$2,
3493
3807
  };
3494
3808
 
3495
3809
  /**
@@ -3527,6 +3841,7 @@ const displayCardEntry$1 = {
3527
3841
  const ACCORDION_SUMMARY_ATTRIBUTE = 'accordion-summary-3-resolutions';
3528
3842
  const ACCORDION_DETAIL_ATTRIBUTE = 'accordion-detail-3-resolutions';
3529
3843
  const ACCORDION_LINKS_ATTRIBUTE = 'accordion-links-3-resolutions';
3844
+ const ACCORDION_INLINE_STYLES_ATTRIBUTE = 'accordion-inline-styles-3-resolutions';
3530
3845
  const ACCORDION_OPEN_ATTRIBUTE = 'accordion-open-3-resolutions';
3531
3846
  const ACCORDION_HEADING_LEVEL_ATTRIBUTE = 'accordion-heading-level-3-resolutions';
3532
3847
  /** What a freshly dropped accordion shows before the author types anything. */
@@ -3654,6 +3969,9 @@ class RAccordionComponent {
3654
3969
  get links() {
3655
3970
  return this.componentAttributes?.[ACCORDION_LINKS_ATTRIBUTE];
3656
3971
  }
3972
+ get spanStyles() {
3973
+ return this.componentAttributes?.[ACCORDION_INLINE_STYLES_ATTRIBUTE];
3974
+ }
3657
3975
  get open() {
3658
3976
  return resolveAccordionOpen(this.componentAttributes);
3659
3977
  }
@@ -3674,12 +3992,12 @@ class RAccordionComponent {
3674
3992
  </summary>
3675
3993
  @for (paragraph of detail; track $index) {
3676
3994
  <p>
3677
- <rulecms-inline-markup [text]="paragraph" [links]="links" />
3995
+ <rulecms-inline-markup [text]="paragraph" [links]="links" [spanStyles]="spanStyles" />
3678
3996
  </p>
3679
3997
  }
3680
3998
  <ng-content />
3681
3999
  </details>
3682
- `, isInline: true, dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: InlineMarkupComponent, selector: "rulecms-inline-markup", inputs: ["text", "links"] }, { kind: "component", type: HtmlTagComponent, selector: "rulecms-html-tag", inputs: ["tag", "styleProps", "className"] }] }); }
4000
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: InlineMarkupComponent, selector: "rulecms-inline-markup", inputs: ["text", "links", "spanStyles"] }, { kind: "component", type: HtmlTagComponent, selector: "rulecms-html-tag", inputs: ["tag", "styleProps", "className"] }] }); }
3683
4001
  }
3684
4002
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: RAccordionComponent, decorators: [{
3685
4003
  type: Component,
@@ -3700,7 +4018,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
3700
4018
  </summary>
3701
4019
  @for (paragraph of detail; track $index) {
3702
4020
  <p>
3703
- <rulecms-inline-markup [text]="paragraph" [links]="links" />
4021
+ <rulecms-inline-markup [text]="paragraph" [links]="links" [spanStyles]="spanStyles" />
3704
4022
  </p>
3705
4023
  }
3706
4024
  <ng-content />
@@ -3722,15 +4040,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
3722
4040
  * element itself. No configuration changes the answer, so no wrapper div,
3723
4041
  * ever.
3724
4042
  */
3725
- const resolveNoContainer = () => false;
4043
+ const resolveNoContainer$1 = () => false;
3726
4044
 
3727
4045
  /**
3728
4046
  * RAccordion's root is always a real element — `<details>` — so it can always
3729
4047
  * carry the column's className, and the component applies the one it receives.
3730
4048
  */
3731
- const resolveRendersOwnElement = () => true;
4049
+ const resolveRendersOwnElement$1 = () => true;
3732
4050
 
3733
- const defaultAttributeValues = [
4051
+ const defaultAttributeValues$1 = [
3734
4052
  {
3735
4053
  name: ACCORDION_SUMMARY_ATTRIBUTE,
3736
4054
  defaultValues: {
@@ -3760,6 +4078,10 @@ const defaultAttributeValues = [
3760
4078
  name: ACCORDION_LINKS_ATTRIBUTE,
3761
4079
  defaultValues: {},
3762
4080
  },
4081
+ {
4082
+ name: ACCORDION_INLINE_STYLES_ATTRIBUTE,
4083
+ defaultValues: {},
4084
+ },
3763
4085
  ];
3764
4086
 
3765
4087
  /**
@@ -3775,7 +4097,7 @@ const defaultAttributeValues = [
3775
4097
  * documentation registry asserts its pages match that sequence, so moving an
3776
4098
  * entry here moves it in both places at once.
3777
4099
  */
3778
- const attributeEditModeConfigs = [
4100
+ const attributeEditModeConfigs$1 = [
3779
4101
  {
3780
4102
  name: ACCORDION_SUMMARY_ATTRIBUTE,
3781
4103
  heading: 'Summary',
@@ -3806,18 +4128,288 @@ const attributeEditModeConfigs = [
3806
4128
  subHeading: 'Links the detail can point at. To link part of the detail, wrap those words in {1|linked text}, where the number is the position of the entry in this list counting from 1; clearing an entry leaves its position in place so the other numbers keep pointing where they did, for 3 different device resolutions',
3807
4129
  defaultValues: {},
3808
4130
  },
4131
+ {
4132
+ name: ACCORDION_INLINE_STYLES_ATTRIBUTE,
4133
+ heading: 'Class Names & Styles',
4134
+ subHeading: 'Class names and CSS the detail can apply to part of itself, e.g. to give a few words their own colour. To apply an entry, wrap those words in {.1|styled text}, where the number is the position of the entry in this list counting from 1; clearing an entry leaves its position in place so the other numbers keep pointing where they did, for 3 different device resolutions',
4135
+ defaultValues: {},
4136
+ },
3809
4137
  ];
3810
4138
 
3811
- const displayCardEntry = {
4139
+ const displayCardEntry$1 = {
3812
4140
  id: ComponentIdentifiers.rAccordion,
4141
+ resolveNoContainer: resolveNoContainer$1,
4142
+ resolveRendersOwnElement: resolveRendersOwnElement$1,
4143
+ card: RAccordionComponent,
4144
+ defaultAttributeValues: defaultAttributeValues$1,
4145
+ attributeEditModeConfigs: attributeEditModeConfigs$1,
4146
+ };
4147
+
4148
+ /**
4149
+ * The attribute names `r-mount` reads.
4150
+ *
4151
+ * Two, and that is the whole of it: which host component to render, and the
4152
+ * props the author filled in for it. The host application supplies the actual
4153
+ * Angular component through `rulecms-widget`'s `mounts` map. This library never
4154
+ * imports customer code.
4155
+ */
4156
+ const MOUNT_NAME_ATTRIBUTE = 'mount-name-3-resolutions';
4157
+ const MOUNT_PROPS_ATTRIBUTE = 'mount-props-3-resolutions';
4158
+ const MOUNT_PROP_TYPES = ['string', 'number', 'boolean', 'json'];
4159
+ const MOUNT_NAME_PATTERN = /^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/;
4160
+ const FORBIDDEN_PROP_KEYS = new Set([
4161
+ '__proto__',
4162
+ 'constructor',
4163
+ 'prototype',
4164
+ 'key',
4165
+ 'ref',
4166
+ 'children',
4167
+ 'dangerouslySetInnerHTML',
4168
+ ]);
4169
+ const HTML_EVENT_KEYS = new Set([
4170
+ 'onclick',
4171
+ 'onsubmit',
4172
+ 'onload',
4173
+ 'onerror',
4174
+ 'onmouseover',
4175
+ 'onmouseenter',
4176
+ 'onfocus',
4177
+ 'onblur',
4178
+ 'onchange',
4179
+ 'oninput',
4180
+ 'onkeydown',
4181
+ 'onkeyup',
4182
+ ]);
4183
+ const isMountPropType = (value) => typeof value === 'string' && MOUNT_PROP_TYPES.includes(value);
4184
+ const isAllowedMountName = (value) => typeof value === 'string' && MOUNT_NAME_PATTERN.test(value);
4185
+ const isForbiddenMountPropKey = (key) => {
4186
+ if (FORBIDDEN_PROP_KEYS.has(key) || HTML_EVENT_KEYS.has(key.toLowerCase())) {
4187
+ return true;
4188
+ }
4189
+ return /^on[A-Z]/.test(key);
4190
+ };
4191
+ const resolveMountName = (componentAttributes) => {
4192
+ const value = componentAttributes?.[MOUNT_NAME_ATTRIBUTE];
4193
+ return isAllowedMountName(value) ? value : '';
4194
+ };
4195
+ const isMountPropRow = (value) => {
4196
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
4197
+ return false;
4198
+ }
4199
+ const row = value;
4200
+ return (typeof row.key === 'string' &&
4201
+ isMountPropType(row.type) &&
4202
+ typeof row.value === 'string');
4203
+ };
4204
+ const coerceMountPropValue = (row) => {
4205
+ switch (row.type) {
4206
+ case 'string':
4207
+ return row.value;
4208
+ case 'number': {
4209
+ const parsed = Number(row.value);
4210
+ return Number.isFinite(parsed) ? parsed : undefined;
4211
+ }
4212
+ case 'boolean':
4213
+ if (row.value === 'true') {
4214
+ return true;
4215
+ }
4216
+ if (row.value === 'false') {
4217
+ return false;
4218
+ }
4219
+ return undefined;
4220
+ case 'json':
4221
+ try {
4222
+ return JSON.parse(row.value);
4223
+ }
4224
+ catch {
4225
+ return undefined;
4226
+ }
4227
+ default:
4228
+ return undefined;
4229
+ }
4230
+ };
4231
+ const resolveMountProps = (componentAttributes) => {
4232
+ const raw = componentAttributes?.[MOUNT_PROPS_ATTRIBUTE];
4233
+ if (!Array.isArray(raw)) {
4234
+ return {};
4235
+ }
4236
+ const props = {};
4237
+ for (const entry of raw) {
4238
+ if (!isMountPropRow(entry)) {
4239
+ continue;
4240
+ }
4241
+ const key = entry.key.trim();
4242
+ if (!key || isForbiddenMountPropKey(key)) {
4243
+ continue;
4244
+ }
4245
+ const coerced = coerceMountPropValue(entry);
4246
+ if (coerced === undefined) {
4247
+ continue;
4248
+ }
4249
+ props[key] = coerced;
4250
+ }
4251
+ return props;
4252
+ };
4253
+
4254
+ const PLACEHOLDER_STYLE = {
4255
+ boxSizing: 'border-box',
4256
+ border: '1px dashed #94a3b8',
4257
+ borderRadius: 4,
4258
+ padding: '12px 16px',
4259
+ color: '#64748b',
4260
+ fontSize: 13,
4261
+ lineHeight: 1.4,
4262
+ fontFamily: 'system-ui, sans-serif',
4263
+ backgroundColor: '#f8fafc',
4264
+ };
4265
+ const resolveMountRender = ({ componentAttributes, mounts, widgetInstanceProps, styleProps, }) => {
4266
+ const name = resolveMountName(componentAttributes);
4267
+ const hostComponent = name && mounts ? mounts[name] : undefined;
4268
+ return {
4269
+ name,
4270
+ hostComponent,
4271
+ placeholderLabel: name || 'Custom Component',
4272
+ rootStyle: hostComponent ? styleProps : { ...PLACEHOLDER_STYLE, ...styleProps },
4273
+ hostInputs: {
4274
+ ...widgetInstanceProps,
4275
+ ...resolveMountProps(componentAttributes),
4276
+ },
4277
+ shouldWarn: Boolean(name && mounts && !hostComponent),
4278
+ };
4279
+ };
4280
+ const warnUnmatchedMount = (name, registered) => {
4281
+ const nodeEnv = globalThis.process?.env
4282
+ ?.NODE_ENV;
4283
+ if (nodeEnv === 'production') {
4284
+ return;
4285
+ }
4286
+ const available = registered.length ? registered.join(', ') : '(none)';
4287
+ console.warn(`r-mount: no host component registered as "${name}". Registered names: ${available}.`);
4288
+ };
4289
+
4290
+ /**
4291
+ * A leaf that renders a host-app component by name.
4292
+ *
4293
+ * The author sets a name and a list of typed props in the composer. The host
4294
+ * application passes the actual component on `rulecms-widget`'s `mounts` map.
4295
+ * This file never imports customer code: a miss is a placeholder, not a
4296
+ * lookup into executable text.
4297
+ *
4298
+ * The root is always a `<div>`. Author props are never spread onto this div
4299
+ * — only onto the resolved host component via `NgComponentOutlet`.
4300
+ */
4301
+ class RMountComponent {
4302
+ constructor() {
4303
+ this.state = resolveMountRender({});
4304
+ }
4305
+ ngOnChanges() {
4306
+ this.state = resolveMountRender({
4307
+ componentAttributes: this.componentAttributes,
4308
+ mounts: this.mounts,
4309
+ widgetInstanceProps: this.widgetInstanceProps,
4310
+ styleProps: this.styleProps,
4311
+ });
4312
+ if (this.state.shouldWarn && this.mounts) {
4313
+ warnUnmatchedMount(this.state.name, Object.keys(this.mounts));
4314
+ }
4315
+ }
4316
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: RMountComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4317
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.25", type: RMountComponent, isStandalone: true, selector: "rulecms-r-mount", inputs: { componentAttributes: "componentAttributes", styleProps: "styleProps", className: "className", mounts: "mounts", widgetInstanceProps: "widgetInstanceProps" }, usesOnChanges: true, ngImport: i0, template: `
4318
+ <div [ngStyle]="state.rootStyle" [class]="className || undefined">
4319
+ @if (state.hostComponent) {
4320
+ <ng-container *ngComponentOutlet="state.hostComponent; inputs: state.hostInputs" />
4321
+ } @else {
4322
+ {{ state.placeholderLabel }}
4323
+ }
4324
+ </div>
4325
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }] }); }
4326
+ }
4327
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: RMountComponent, decorators: [{
4328
+ type: Component,
4329
+ args: [{
4330
+ selector: 'rulecms-r-mount',
4331
+ standalone: true,
4332
+ imports: [NgStyle, NgComponentOutlet],
4333
+ template: `
4334
+ <div [ngStyle]="state.rootStyle" [class]="className || undefined">
4335
+ @if (state.hostComponent) {
4336
+ <ng-container *ngComponentOutlet="state.hostComponent; inputs: state.hostInputs" />
4337
+ } @else {
4338
+ {{ state.placeholderLabel }}
4339
+ }
4340
+ </div>
4341
+ `,
4342
+ }]
4343
+ }], propDecorators: { componentAttributes: [{
4344
+ type: Input
4345
+ }], styleProps: [{
4346
+ type: Input
4347
+ }], className: [{
4348
+ type: Input
4349
+ }], mounts: [{
4350
+ type: Input
4351
+ }], widgetInstanceProps: [{
4352
+ type: Input
4353
+ }] } });
4354
+
4355
+ /**
4356
+ * Always renders a root `<div>` that holds the column's box styles — either
4357
+ * around the host component or around the placeholder. The host component is
4358
+ * unknown to this library, so it cannot be trusted to carry styles itself.
4359
+ */
4360
+ const resolveNoContainer = () => false;
4361
+
4362
+ /**
4363
+ * The root `<div>` carries the className. There is always exactly one element
4364
+ * and it is always the root, whether the host component resolved or not.
4365
+ */
4366
+ const resolveRendersOwnElement = () => true;
4367
+
4368
+ /**
4369
+ * A fresh mount has no name and no props. Seeding either would invent a
4370
+ * registration the host does not have, so both stay empty until the author
4371
+ * fills them in.
4372
+ */
4373
+ const defaultAttributeValues = [
4374
+ {
4375
+ name: MOUNT_NAME_ATTRIBUTE,
4376
+ defaultValues: {},
4377
+ },
4378
+ {
4379
+ name: MOUNT_PROPS_ATTRIBUTE,
4380
+ defaultValues: {},
4381
+ },
4382
+ ];
4383
+
4384
+ /**
4385
+ * Both settings the mount offers, in the order the Modify drawer shows them.
4386
+ */
4387
+ const attributeEditModeConfigs = [
4388
+ {
4389
+ name: MOUNT_NAME_ATTRIBUTE,
4390
+ heading: 'Component Name',
4391
+ subHeading: 'The name your developer registered in the app. Must match exactly, for 3 different device resolutions',
4392
+ defaultValues: {},
4393
+ },
4394
+ {
4395
+ name: MOUNT_PROPS_ATTRIBUTE,
4396
+ heading: 'Props',
4397
+ subHeading: 'Name and value pairs passed to that component. The component decides what each name means, for 3 different device resolutions',
4398
+ defaultValues: {},
4399
+ },
4400
+ ];
4401
+
4402
+ const displayCardEntry = {
4403
+ id: ComponentIdentifiers.rMount,
3813
4404
  resolveNoContainer,
3814
4405
  resolveRendersOwnElement,
3815
- card: RAccordionComponent,
4406
+ card: RMountComponent,
3816
4407
  defaultAttributeValues,
3817
4408
  attributeEditModeConfigs,
3818
4409
  };
3819
4410
 
3820
4411
  const cardEntries$1 = [
4412
+ displayCardEntry$9,
3821
4413
  displayCardEntry$8,
3822
4414
  displayCardEntry$7,
3823
4415
  displayCardEntry$6,
@@ -3904,6 +4496,7 @@ const componentNameToModifyAttribute = {
3904
4496
  'embed-loading-3-resolutions': 'embed-loading-3-resolutions',
3905
4497
  'enclosing-tag-3-resolutions': 'enclosing-tag-3-resolutions',
3906
4498
  'links-3-resolutions': 'links-3-resolutions',
4499
+ 'inline-styles-3-resolutions': 'inline-styles-3-resolutions',
3907
4500
  // r-list owns its own attribute names — one component per documented
3908
4501
  // setting — but reuses existing editors wherever one fits: the multi-line
3909
4502
  // text field for its items, the numbered link list for its links, and the
@@ -3916,6 +4509,7 @@ const componentNameToModifyAttribute = {
3916
4509
  'list-start-3-resolutions': 'image-text-3-resolutions',
3917
4510
  'list-reversed-3-resolutions': 'list-reversed-3-resolutions',
3918
4511
  'list-links-3-resolutions': 'links-3-resolutions',
4512
+ 'list-inline-styles-3-resolutions': 'inline-styles-3-resolutions',
3919
4513
  // r-accordion owns its own attribute names — one component per documented
3920
4514
  // setting — and reuses existing editors for everything that fits: the
3921
4515
  // multi-line text field for the summary and the detail, and the numbered
@@ -3926,6 +4520,12 @@ const componentNameToModifyAttribute = {
3926
4520
  'accordion-open-3-resolutions': 'accordion-open-3-resolutions',
3927
4521
  'accordion-heading-level-3-resolutions': 'accordion-heading-level-3-resolutions',
3928
4522
  'accordion-links-3-resolutions': 'links-3-resolutions',
4523
+ 'accordion-inline-styles-3-resolutions': 'inline-styles-3-resolutions',
4524
+ // r-mount: the name is a constrained identifier and reuses the shared
4525
+ // plain-text editor; the props list is a repeatable key/type/value row
4526
+ // and needs its own editor.
4527
+ 'mount-name-3-resolutions': 'image-text-3-resolutions',
4528
+ 'mount-props-3-resolutions': 'mount-props-3-resolutions',
3929
4529
  'content-title-3-resolutions': 'content-title-3-resolutions',
3930
4530
  'media-selector-3-resolutions': 'media-selector-3-resolutions',
3931
4531
  'pass-through-image-attributes-alt-3-resolutions': 'image-alt-text-3-resolutions',
@@ -3999,6 +4599,7 @@ const cardEntries = [
3999
4599
  { id: ComponentIdentifiers.rEmbed, icon: '⧉', label: 'Embed' },
4000
4600
  { id: ComponentIdentifiers.rList, icon: '☰', label: 'List' },
4001
4601
  { id: ComponentIdentifiers.rAccordion, icon: '▸', label: 'Accordion' },
4602
+ { id: ComponentIdentifiers.rMount, icon: '⌘', label: 'Custom' },
4002
4603
  ];
4003
4604
 
4004
4605
  const cardGroups = [
@@ -4128,17 +4729,18 @@ const markLetters = (marks) => MARK_LETTERS.filter(([mark]) => marks[mark])
4128
4729
  .map(([, letter]) => letter)
4129
4730
  .join('');
4130
4731
  /**
4131
- * A span back to its marker. Text that carries neither marks nor an index is
4132
- * emitted bare: a marker with nothing to say would only be noise an author has
4133
- * to read past, and it round-trips to the same runs either way.
4732
+ * A span back to its marker. Text that carries no marks, no link and no styling
4733
+ * is emitted bare: a marker with nothing to say would only be noise an author
4734
+ * has to read past, and it round-trips to the same runs either way.
4134
4735
  */
4135
4736
  const formatSpan = (run) => {
4136
4737
  const letters = markLetters(run.marks);
4137
4738
  const index = run.index === undefined ? '' : `${run.index}`;
4138
- if (letters === '' && index === '') {
4739
+ const styleIndex = run.styleIndex === undefined ? '' : `.${run.styleIndex}`;
4740
+ if (letters === '' && index === '' && styleIndex === '') {
4139
4741
  return run.text;
4140
4742
  }
4141
- return `{${letters}${index}|${run.text}}`;
4743
+ return `{${letters}${index}${styleIndex}|${run.text}}`;
4142
4744
  };
4143
4745
  /** One run as authored text. */
4144
4746
  function formatInlineMarker(run) {
@@ -4168,5 +4770,5 @@ function serializeInlineMarkers(runs) {
4168
4770
  * Generated bundle index. Do not edit.
4169
4771
  */
4170
4772
 
4171
- export { ACCORDION_HEADING_LEVELS, BLOCK_ENCLOSING_TAGS, BUTTON_BORDER_VARIABLE, BUTTON_CLICK_HANDLER_PROP, BUTTON_CSS_VARIABLE_PREFIX, BUTTON_FILL_ACTIVE_VARIABLE, BUTTON_FILL_HOVER_VARIABLE, BUTTON_FILL_VARIABLE, BUTTON_INK_HOVER_VARIABLE, BUTTON_INK_VARIABLE, BUTTON_LIBRARY_STYLESHEET, BUTTON_RING_VARIABLE, BUTTON_SIZES, BUTTON_TONES, BUTTON_VARIANTS, ComponentIdentifiers, DEFAULT_ACCORDION_HEADING_LEVEL, DEFAULT_BUTTON_LABEL, DEFAULT_BUTTON_PRESET, DEFAULT_EMBED_LOADING, DEFAULT_ICON_POSITION, DEFAULT_LIST_KIND, EMBED_ASPECT_RATIOS, EMBED_LOADING_VALUES, EMBED_PROVIDERS, EMBED_UNSHAPED_HEIGHT, ENCLOSING_TAGS, ICON_POSITIONS, INLINE_ENCLOSING_TAGS, InlineMarkupComponent, LIST_KINDS, LIST_MARKERS, REJECTED_HREF_SCHEMES, SIZE_SCALES, TONE_PALETTES, applyButtonColumnStyles, buildButtonPresetStyles, buildButtonRenderStyles, disabledStyles as buttonDisabledStyles, cardGroups$1 as cardGroups, components, editor, formatInlineMarker, getComponentMetadataForEditing, getPreviewCardGroups, getPreviewComponents, getSourceComponentsPlugin, isAllowedEmbedUrl, isBlockEnclosingTag, isButtonSize, isButtonTone, isButtonVariant, isEnclosingTag, isSafeHref, manifest, normaliseHost, parseInlineLinkMarkers, parseInlineMarkers, resolveButtonPreset, resolveEmbedUrl, resolveLinkAttributes, serializeInlineMarkers, stylesheet, toneCssVariable };
4773
+ export { ACCORDION_HEADING_LEVELS, BLOCK_ENCLOSING_TAGS, BUTTON_BORDER_VARIABLE, BUTTON_CLICK_HANDLER_PROP, BUTTON_CSS_VARIABLE_PREFIX, BUTTON_FILL_ACTIVE_VARIABLE, BUTTON_FILL_HOVER_VARIABLE, BUTTON_FILL_VARIABLE, BUTTON_INK_HOVER_VARIABLE, BUTTON_INK_VARIABLE, BUTTON_LIBRARY_STYLESHEET, BUTTON_RING_VARIABLE, BUTTON_SIZES, BUTTON_TONES, BUTTON_VARIANTS, ComponentIdentifiers, DEFAULT_ACCORDION_HEADING_LEVEL, DEFAULT_BUTTON_LABEL, DEFAULT_BUTTON_PRESET, DEFAULT_EMBED_LOADING, DEFAULT_ICON_POSITION, DEFAULT_LIST_KIND, EMBED_ASPECT_RATIOS, EMBED_LOADING_VALUES, EMBED_PROVIDERS, EMBED_UNSHAPED_HEIGHT, ENCLOSING_TAGS, ICON_POSITIONS, INLINE_ENCLOSING_TAGS, InlineMarkupComponent, LIST_KINDS, LIST_MARKERS, MOUNT_NAME_ATTRIBUTE, MOUNT_NAME_PATTERN, MOUNT_PROPS_ATTRIBUTE, MOUNT_PROP_TYPES, REJECTED_HREF_SCHEMES, SIZE_SCALES, TONE_PALETTES, applyButtonColumnStyles, buildButtonPresetStyles, buildButtonRenderStyles, disabledStyles as buttonDisabledStyles, cardGroups$1 as cardGroups, components, editor, formatInlineMarker, formatStyleDeclarations, getComponentMetadataForEditing, getPreviewCardGroups, getPreviewComponents, getSourceComponentsPlugin, getSpanStyleItemAt, isAllowedEmbedUrl, isAllowedMountName, isBlockEnclosingTag, isButtonSize, isButtonTone, isButtonVariant, isEmptySpanStyleItem, isEnclosingTag, isForbiddenMountPropKey, isSafeHref, isSpanStyleItem, manifest, normaliseHost, parseInlineLinkMarkers, parseInlineMarkers, parseStyleDeclarations, resolveButtonPreset, resolveClassNames, resolveEmbedUrl, resolveLinkAttributes, resolveMountName, resolveMountProps, resolveSpanStyleAttributes, serializeInlineMarkers, stylesheet, toneCssVariable };
4172
4774
  //# sourceMappingURL=rulecms-source-components-angular.mjs.map