@rulecms/source-components-angular 0.4.0 → 0.5.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.
- package/fesm2022/rulecms-source-components-angular.mjs +1042 -66
- package/fesm2022/rulecms-source-components-angular.mjs.map +1 -1
- package/lib/base-components/display/component-attributes/component-name-to-modify-attribute.d.ts.map +1 -1
- package/lib/base-components/display/components/r-accordion/accordion-attributes.d.ts +1 -1
- package/lib/base-components/display/components/r-accordion/accordion-library-stylesheet.d.ts +13 -0
- package/lib/base-components/display/components/r-accordion/accordion-library-stylesheet.d.ts.map +1 -0
- package/lib/base-components/display/components/r-accordion/r-accordion.component.d.ts +11 -1
- package/lib/base-components/display/components/r-accordion/r-accordion.component.d.ts.map +1 -1
- package/lib/base-components/display/components/r-button/r-button.component.d.ts +3 -1
- package/lib/base-components/display/components/r-button/r-button.component.d.ts.map +1 -1
- package/lib/base-components/display/components/r-text/attribute-edit-mode-configs.d.ts.map +1 -1
- package/lib/base-components/display/components/r-text/default-attribute-values.d.ts.map +1 -1
- package/lib/base-components/display/components/r-text/r-text.component.d.ts +36 -2
- package/lib/base-components/display/components/r-text/r-text.component.d.ts.map +1 -1
- package/lib/base-components/display/components/r-text/resolve-renders-own-element.d.ts +7 -4
- package/lib/base-components/display/components/r-text/resolve-renders-own-element.d.ts.map +1 -1
- package/lib/base-components/display/components/r-text/text-attributes.d.ts +49 -0
- package/lib/base-components/display/components/r-text/text-attributes.d.ts.map +1 -0
- package/lib/base-components/display/components/r-text/text-library-stylesheet.d.ts +15 -0
- package/lib/base-components/display/components/r-text/text-library-stylesheet.d.ts.map +1 -0
- package/lib/component-library-module.d.ts +1 -6
- package/lib/component-library-module.d.ts.map +1 -1
- package/lib/library-stylesheet.d.ts +7 -0
- package/lib/library-stylesheet.d.ts.map +1 -0
- package/lib/library-version.d.ts +3 -0
- package/lib/library-version.d.ts.map +1 -0
- package/lib/shared/html-tag.component.d.ts +5 -1
- package/lib/shared/html-tag.component.d.ts.map +1 -1
- package/lib/shared/inline-markup/index.d.ts +3 -0
- package/lib/shared/inline-markup/index.d.ts.map +1 -1
- package/lib/shared/inline-markup/inline-markup.component.d.ts +8 -1
- package/lib/shared/inline-markup/inline-markup.component.d.ts.map +1 -1
- package/lib/shared/inline-markup/parse-inline-markers.d.ts +6 -1
- package/lib/shared/inline-markup/parse-inline-markers.d.ts.map +1 -1
- package/lib/shared/inline-markup/prepared-inline-markup.d.ts +32 -0
- package/lib/shared/inline-markup/prepared-inline-markup.d.ts.map +1 -0
- package/lib/shared/inline-markup/render-inline-markup-split.d.ts +33 -0
- package/lib/shared/inline-markup/render-inline-markup-split.d.ts.map +1 -0
- package/lib/shared/placeholders/fill-placeholders.d.ts +15 -0
- package/lib/shared/placeholders/fill-placeholders.d.ts.map +1 -0
- package/lib/shared/placeholders/index.d.ts +4 -0
- package/lib/shared/placeholders/index.d.ts.map +1 -0
- package/lib/shared/placeholders/parse-placeholder-expression.d.ts +26 -0
- package/lib/shared/placeholders/parse-placeholder-expression.d.ts.map +1 -0
- package/lib/shared/placeholders/placeholder-token.d.ts +15 -0
- package/lib/shared/placeholders/placeholder-token.d.ts.map +1 -0
- package/lib/shared/placeholders/placeholder-values.d.ts +23 -0
- package/lib/shared/placeholders/placeholder-values.d.ts.map +1 -0
- package/lib/shared/placeholders/placeholder-warnings.d.ts +10 -0
- package/lib/shared/placeholders/placeholder-warnings.d.ts.map +1 -0
- package/lib/types.d.ts +6 -0
- package/lib/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/public-api.d.ts +5 -1
- package/public-api.d.ts.map +1 -1
|
@@ -1,8 +1,99 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { Input, Component, ViewChild } from '@angular/core';
|
|
3
|
-
import { NgStyle, NgComponentOutlet } from '@angular/common';
|
|
3
|
+
import { NgStyle, NgTemplateOutlet, NgComponentOutlet } from '@angular/common';
|
|
4
4
|
import * as i1 from '@angular/platform-browser';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* The attribute names `r-text` reads for show more / show less.
|
|
8
|
+
*
|
|
9
|
+
* These sit beside the existing text attributes rather than sharing a name
|
|
10
|
+
* with any other component: RuleCMS publishes one page per component-scope
|
|
11
|
+
* attribute and requires exactly one owning component. Reuse happens a level
|
|
12
|
+
* down, where the composer resolves an attribute to an *editor* by name —
|
|
13
|
+
* the limit reuses the plain-text field and the labels reuse the multi-line
|
|
14
|
+
* text field.
|
|
15
|
+
*
|
|
16
|
+
* Nothing here duplicates a column style attribute. Spacing, colour and
|
|
17
|
+
* typography come from Column Styles on the root element; the attributes
|
|
18
|
+
* below are only the character budget and the two toggle labels.
|
|
19
|
+
*/
|
|
20
|
+
const TEXT_SHOW_MORE_AFTER_ATTRIBUTE = 'text-show-more-after-3-resolutions';
|
|
21
|
+
const TEXT_SHOW_MORE_LABEL_ATTRIBUTE = 'text-show-more-label-3-resolutions';
|
|
22
|
+
const TEXT_SHOW_LESS_LABEL_ATTRIBUTE = 'text-show-less-label-3-resolutions';
|
|
23
|
+
const DEFAULT_TEXT_SHOW_MORE_LABEL = 'Show more';
|
|
24
|
+
const DEFAULT_TEXT_SHOW_LESS_LABEL = 'Show less';
|
|
25
|
+
/**
|
|
26
|
+
* Collapses a label to one line the way a toggle's visible text is one line:
|
|
27
|
+
* authored newlines become spaces, a `{br|}` becomes a space, then trim.
|
|
28
|
+
* Markers for bold, italic and styling stay in the string so render can
|
|
29
|
+
* honour them; link markers stay too and print as plain words when no link
|
|
30
|
+
* list is passed.
|
|
31
|
+
*/
|
|
32
|
+
const resolveOneLineLabel = (value) => {
|
|
33
|
+
const text = typeof value === 'string' ? value : String(value);
|
|
34
|
+
return text
|
|
35
|
+
.replace(/\{br\|\}/g, ' ')
|
|
36
|
+
.replace(/\s+/g, ' ')
|
|
37
|
+
.trim();
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Character budget for collapsing the text. A positive whole number turns
|
|
41
|
+
* the feature on; anything else — unset, null, empty, zero, a decimal, a
|
|
42
|
+
* non-digit string — leaves the text as it always was.
|
|
43
|
+
*
|
|
44
|
+
* The editor stores a string in the plain-text field (the number editor is
|
|
45
|
+
* pixel-specific), but a hand-written or migrated config may carry a real
|
|
46
|
+
* number; both mean the same thing here. Digit-only strings after trim are
|
|
47
|
+
* accepted, including leading zeros (`'07'` → 7); `'0'` and `'00'` are still
|
|
48
|
+
* off because the value is not >= 1.
|
|
49
|
+
*/
|
|
50
|
+
const resolveTextShowMoreAfter = (componentAttributes) => {
|
|
51
|
+
const value = componentAttributes?.[TEXT_SHOW_MORE_AFTER_ATTRIBUTE];
|
|
52
|
+
if (typeof value === 'number') {
|
|
53
|
+
if (Number.isFinite(value) && Number.isInteger(value) && value >= 1) {
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
if (typeof value === 'string') {
|
|
59
|
+
const trimmed = value.trim();
|
|
60
|
+
if (/^\d+$/.test(trimmed)) {
|
|
61
|
+
const parsed = Number(trimmed);
|
|
62
|
+
if (parsed >= 1) {
|
|
63
|
+
return parsed;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return undefined;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Label on the toggle while the text is collapsed. Unset or null falls back
|
|
71
|
+
* to "Show more". A blank after trimming also falls back — a toggle with no
|
|
72
|
+
* words would leave the rest of the text unreachable.
|
|
73
|
+
*/
|
|
74
|
+
const resolveTextShowMoreLabel = (componentAttributes) => {
|
|
75
|
+
const value = componentAttributes?.[TEXT_SHOW_MORE_LABEL_ATTRIBUTE];
|
|
76
|
+
if (value === undefined || value === null) {
|
|
77
|
+
return DEFAULT_TEXT_SHOW_MORE_LABEL;
|
|
78
|
+
}
|
|
79
|
+
const normalized = resolveOneLineLabel(value);
|
|
80
|
+
return normalized === '' ? DEFAULT_TEXT_SHOW_MORE_LABEL : normalized;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Label on the toggle while the text is expanded. Unset or null falls back
|
|
84
|
+
* to "Show less". A blank after trimming also falls back — the composer's
|
|
85
|
+
* text editor deletes a cleared setting rather than storing `''`, so a blank
|
|
86
|
+
* here is the same kind of empty as Show more, not a distinct mode.
|
|
87
|
+
*/
|
|
88
|
+
const resolveTextShowLessLabel = (componentAttributes) => {
|
|
89
|
+
const value = componentAttributes?.[TEXT_SHOW_LESS_LABEL_ATTRIBUTE];
|
|
90
|
+
if (value === undefined || value === null) {
|
|
91
|
+
return DEFAULT_TEXT_SHOW_LESS_LABEL;
|
|
92
|
+
}
|
|
93
|
+
const normalized = resolveOneLineLabel(value);
|
|
94
|
+
return normalized === '' ? DEFAULT_TEXT_SHOW_LESS_LABEL : normalized;
|
|
95
|
+
};
|
|
96
|
+
|
|
6
97
|
const DEFAULT_CONTENT_TEXT = 'This is a text block. Click to edit and add your own content.';
|
|
7
98
|
const defaultAttributeValues$9 = [
|
|
8
99
|
{
|
|
@@ -23,6 +114,18 @@ const defaultAttributeValues$9 = [
|
|
|
23
114
|
name: 'inline-styles-3-resolutions',
|
|
24
115
|
defaultValues: {},
|
|
25
116
|
},
|
|
117
|
+
{
|
|
118
|
+
name: TEXT_SHOW_MORE_AFTER_ATTRIBUTE,
|
|
119
|
+
defaultValues: {},
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: TEXT_SHOW_MORE_LABEL_ATTRIBUTE,
|
|
123
|
+
defaultValues: {},
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: TEXT_SHOW_LESS_LABEL_ATTRIBUTE,
|
|
127
|
+
defaultValues: {},
|
|
128
|
+
},
|
|
26
129
|
];
|
|
27
130
|
|
|
28
131
|
/**
|
|
@@ -602,19 +705,35 @@ function resolveSpanStyleAttributes(item) {
|
|
|
602
705
|
};
|
|
603
706
|
}
|
|
604
707
|
|
|
708
|
+
/**
|
|
709
|
+
* A placeholder token: `{{`, anything but a brace, `}}`.
|
|
710
|
+
*
|
|
711
|
+
* Found by shape alone. Whether what sits between the braces is a usable
|
|
712
|
+
* expression is `parsePlaceholderExpression`'s decision, so the marker parser
|
|
713
|
+
* and the filler always agree on where a token starts and ends — including for
|
|
714
|
+
* a token that turns out to be a typo and stays literal.
|
|
715
|
+
*
|
|
716
|
+
* A source string rather than a RegExp so the marker grammar can embed it: a
|
|
717
|
+
* marker's text may contain whole tokens, which is what lets
|
|
718
|
+
* `{b|{{ user.firstName }}}` bold a filled-in value instead of ending the
|
|
719
|
+
* marker at the token's first `}`.
|
|
720
|
+
*/
|
|
721
|
+
const PLACEHOLDER_TOKEN_PATTERN = String.raw `\{\{[^{}]*\}\}`;
|
|
722
|
+
|
|
605
723
|
/**
|
|
606
724
|
* `{` + optional mark letters + optional link index + optional `.` and styling
|
|
607
725
|
* index + `|` + text + `}`.
|
|
608
726
|
*
|
|
609
727
|
* Sticky, so it can be tried at one position without slicing the string. The
|
|
610
|
-
* text is everything up to the first `}
|
|
611
|
-
* non-nesting
|
|
728
|
+
* text is everything up to the first `}` that is not part of a whole
|
|
729
|
+
* `{{ … }}` placeholder token, which is what makes markers non-nesting while
|
|
730
|
+
* still letting a placeholder sit inside one: `{b|{{ user.firstName }}}`.
|
|
612
731
|
*
|
|
613
732
|
* The dot is what tells the two indexes apart, and it reads as the CSS class
|
|
614
733
|
* selector it usually stands for. It cannot be confused with a link index,
|
|
615
734
|
* because a link index is bare digits and always comes first.
|
|
616
735
|
*/
|
|
617
|
-
const MARKER =
|
|
736
|
+
const MARKER = new RegExp(String.raw `\{([a-z]*)(\d*)(?:\.(\d+))?\|((?:${PLACEHOLDER_TOKEN_PATTERN}|[^}])*)\}`, 'y');
|
|
618
737
|
/**
|
|
619
738
|
* One letter per mark. Order is not significant (`{bi1|x}` and `{ib1|x}` are
|
|
620
739
|
* the same span) and repeats are harmless, because the letters resolve to a
|
|
@@ -663,7 +782,12 @@ const resolveMarks = (letters) => {
|
|
|
663
782
|
*
|
|
664
783
|
* Indexes are 1-based and normalize through a base-10 parse, so `{01|x}` and
|
|
665
784
|
* `{1|x}` point at the same entry. Resolving an index to an entry is the
|
|
666
|
-
* caller's job; this stays pure and free of
|
|
785
|
+
* caller's job; this stays pure and free of the renderer.
|
|
786
|
+
*
|
|
787
|
+
* `{{ … }}` placeholders are not runs: they stay inside the text of whichever
|
|
788
|
+
* run holds them, exactly as written, and a marker's text may contain whole
|
|
789
|
+
* ones. Filling them in is the renderer's job, after parsing, so a filled-in
|
|
790
|
+
* value can never be read as markup.
|
|
667
791
|
*/
|
|
668
792
|
function parseInlineMarkers(value) {
|
|
669
793
|
if (typeof value !== 'string' || value === '') {
|
|
@@ -737,13 +861,48 @@ const resolveRun = (letters, digits, styleDigits, text) => {
|
|
|
737
861
|
};
|
|
738
862
|
};
|
|
739
863
|
|
|
864
|
+
const hasMark = (marks) => marks.bold || marks.italic || marks.strike;
|
|
865
|
+
/**
|
|
866
|
+
* Parses markers, fills words, and resolves anchors and span styles into a
|
|
867
|
+
* list the renderers can share. Empty filled words stay in the list so run
|
|
868
|
+
* indexes stay stable for splitting; they contribute nothing when rendered.
|
|
869
|
+
*/
|
|
870
|
+
function prepareInlineMarkupRuns(text, links, spanStyles, fillText) {
|
|
871
|
+
const runs = parseInlineMarkers(text);
|
|
872
|
+
return runs.map((run) => {
|
|
873
|
+
if (run.kind === 'break') {
|
|
874
|
+
return { kind: 'break' };
|
|
875
|
+
}
|
|
876
|
+
const words = fillText === undefined ? run.text : fillText(run.text);
|
|
877
|
+
if (run.kind === 'text') {
|
|
878
|
+
return { kind: 'text', words };
|
|
879
|
+
}
|
|
880
|
+
return {
|
|
881
|
+
kind: 'span',
|
|
882
|
+
words,
|
|
883
|
+
marks: run.marks,
|
|
884
|
+
anchor: run.index !== undefined
|
|
885
|
+
? resolveLinkAttributes(getLinkItemAt(links, run.index))
|
|
886
|
+
: undefined,
|
|
887
|
+
spanStyle: run.styleIndex !== undefined
|
|
888
|
+
? resolveSpanStyleAttributes(getSpanStyleItemAt(spanStyles, run.styleIndex))
|
|
889
|
+
: undefined,
|
|
890
|
+
};
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
/** True when rendering these runs needs elements rather than one text node. */
|
|
894
|
+
function preparedRunsNeedElements(runs) {
|
|
895
|
+
return runs.some((run) => run.kind === 'break' ||
|
|
896
|
+
(run.kind === 'span' &&
|
|
897
|
+
run.words !== '' &&
|
|
898
|
+
(run.anchor !== undefined || run.spanStyle !== undefined || hasMark(run.marks))));
|
|
899
|
+
}
|
|
900
|
+
|
|
740
901
|
const MARK_TAGS = [
|
|
741
902
|
['bold', 'strong'],
|
|
742
903
|
['italic', 'em'],
|
|
743
904
|
['strike', 's'],
|
|
744
905
|
];
|
|
745
|
-
const hasMark = (marks) => marks.bold || marks.italic || marks.strike;
|
|
746
|
-
const plainTextOf = (run) => (run.kind === 'break' ? '' : run.text);
|
|
747
906
|
class InlineMarkupComponent {
|
|
748
907
|
constructor(host, renderer) {
|
|
749
908
|
this.host = host;
|
|
@@ -752,32 +911,25 @@ class InlineMarkupComponent {
|
|
|
752
911
|
}
|
|
753
912
|
ngOnChanges() {
|
|
754
913
|
this.clear();
|
|
755
|
-
const runs =
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
:
|
|
759
|
-
const spanStyles = runs.map((run) => run.kind === 'span' && run.styleIndex !== undefined
|
|
760
|
-
? resolveSpanStyleAttributes(getSpanStyleItemAt(this.spanStyles, run.styleIndex))
|
|
761
|
-
: undefined);
|
|
762
|
-
const needsElement = runs.some((run, position) => run.kind === 'break' ||
|
|
763
|
-
(run.kind === 'span' &&
|
|
764
|
-
(anchors[position] !== undefined ||
|
|
765
|
-
spanStyles[position] !== undefined ||
|
|
766
|
-
hasMark(run.marks))));
|
|
767
|
-
if (!needsElement) {
|
|
768
|
-
this.renderer.appendChild(this.host.nativeElement, this.renderer.createText(runs.map(plainTextOf).join('')));
|
|
914
|
+
const runs = this.runs ??
|
|
915
|
+
prepareInlineMarkupRuns(this.text, this.links, this.spanStyles, this.fillText);
|
|
916
|
+
if (!preparedRunsNeedElements(runs)) {
|
|
917
|
+
this.renderer.appendChild(this.host.nativeElement, this.renderer.createText(runs.map((run) => (run.kind === 'break' ? '' : run.words)).join('')));
|
|
769
918
|
return;
|
|
770
919
|
}
|
|
771
|
-
runs.forEach((run
|
|
920
|
+
runs.forEach((run) => {
|
|
772
921
|
if (run.kind === 'text') {
|
|
773
|
-
this.renderer.appendChild(this.host.nativeElement, this.renderer.createText(run.
|
|
922
|
+
this.renderer.appendChild(this.host.nativeElement, this.renderer.createText(run.words));
|
|
774
923
|
return;
|
|
775
924
|
}
|
|
776
925
|
if (run.kind === 'break') {
|
|
777
926
|
this.renderer.appendChild(this.host.nativeElement, this.renderer.createElement('br'));
|
|
778
927
|
return;
|
|
779
928
|
}
|
|
780
|
-
|
|
929
|
+
if (run.words === '') {
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
this.renderer.appendChild(this.host.nativeElement, this.renderSpan(run));
|
|
781
933
|
});
|
|
782
934
|
}
|
|
783
935
|
/**
|
|
@@ -785,10 +937,10 @@ class InlineMarkupComponent {
|
|
|
785
937
|
* element: the marks say what these words *are*, and the styling says how
|
|
786
938
|
* this run of words looks, which is the container of the other two.
|
|
787
939
|
*/
|
|
788
|
-
renderSpan(run
|
|
789
|
-
let node = anchor === undefined
|
|
790
|
-
? this.renderer.createText(run.
|
|
791
|
-
: this.createAnchor(anchor, run.
|
|
940
|
+
renderSpan(run) {
|
|
941
|
+
let node = run.anchor === undefined
|
|
942
|
+
? this.renderer.createText(run.words)
|
|
943
|
+
: this.createAnchor(run.anchor, run.words);
|
|
792
944
|
for (let position = MARK_TAGS.length - 1; position >= 0; position -= 1) {
|
|
793
945
|
const [mark, tag] = MARK_TAGS[position];
|
|
794
946
|
if (run.marks[mark]) {
|
|
@@ -797,14 +949,14 @@ class InlineMarkupComponent {
|
|
|
797
949
|
node = wrapper;
|
|
798
950
|
}
|
|
799
951
|
}
|
|
800
|
-
if (spanStyle === undefined) {
|
|
952
|
+
if (run.spanStyle === undefined) {
|
|
801
953
|
return node;
|
|
802
954
|
}
|
|
803
955
|
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);
|
|
956
|
+
if (run.spanStyle.className)
|
|
957
|
+
this.renderer.setAttribute(styled, 'class', run.spanStyle.className);
|
|
958
|
+
if (run.spanStyle.style)
|
|
959
|
+
this.renderer.setAttribute(styled, 'style', run.spanStyle.style);
|
|
808
960
|
this.renderer.appendChild(styled, node);
|
|
809
961
|
return styled;
|
|
810
962
|
}
|
|
@@ -837,7 +989,7 @@ class InlineMarkupComponent {
|
|
|
837
989
|
}
|
|
838
990
|
}
|
|
839
991
|
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 }); }
|
|
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 }); }
|
|
992
|
+
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", fillText: "fillText", runs: "runs" }, host: { styleAttribute: "display: contents" }, usesOnChanges: true, ngImport: i0, template: '', isInline: true }); }
|
|
841
993
|
}
|
|
842
994
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: InlineMarkupComponent, decorators: [{
|
|
843
995
|
type: Component,
|
|
@@ -853,8 +1005,300 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
853
1005
|
type: Input
|
|
854
1006
|
}], spanStyles: [{
|
|
855
1007
|
type: Input
|
|
1008
|
+
}], fillText: [{
|
|
1009
|
+
type: Input
|
|
1010
|
+
}], runs: [{
|
|
1011
|
+
type: Input
|
|
856
1012
|
}] } });
|
|
857
1013
|
|
|
1014
|
+
const isWhitespaceChar = (char) => /\s/u.test(char);
|
|
1015
|
+
const unitIsWhitespace = (unit) => unit.kind === 'break' || isWhitespaceChar(unit.char);
|
|
1016
|
+
/**
|
|
1017
|
+
* Callers validate `limit` as a positive integer; still refuse anything else so
|
|
1018
|
+
* a bad budget never invents a cut.
|
|
1019
|
+
*/
|
|
1020
|
+
const isPositiveIntegerLimit = (limit) => typeof limit === 'number' && Number.isFinite(limit) && Number.isInteger(limit) && limit >= 1;
|
|
1021
|
+
/**
|
|
1022
|
+
* Flattens prepared runs into the code-point stream a reader sees. Empty filled
|
|
1023
|
+
* words contribute nothing; each `{br|}` is one unit that counts as whitespace.
|
|
1024
|
+
*/
|
|
1025
|
+
const flattenUnits = (runs) => {
|
|
1026
|
+
const units = [];
|
|
1027
|
+
for (let runIndex = 0; runIndex < runs.length; runIndex += 1) {
|
|
1028
|
+
const run = runs[runIndex];
|
|
1029
|
+
if (run.kind === 'break') {
|
|
1030
|
+
units.push({ kind: 'break', runIndex });
|
|
1031
|
+
continue;
|
|
1032
|
+
}
|
|
1033
|
+
const codePoints = Array.from(run.words);
|
|
1034
|
+
for (let offset = 0; offset < codePoints.length; offset += 1) {
|
|
1035
|
+
units.push({ kind: 'char', runIndex, offset, char: codePoints[offset] });
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
return units;
|
|
1039
|
+
};
|
|
1040
|
+
/**
|
|
1041
|
+
* Marks units that sit inside a whole `{{ … }}` token. Whitespace inside a
|
|
1042
|
+
* token is still whitespace for counting, but it must not be a cut point —
|
|
1043
|
+
* otherwise a composer-canvas budget can land on "Welcome back, {{…".
|
|
1044
|
+
*/
|
|
1045
|
+
const markUnitsInsidePlaceholderTokens = (runs, units) => {
|
|
1046
|
+
const inside = new Array(units.length).fill(false);
|
|
1047
|
+
const unitIndexByRunOffset = new Map();
|
|
1048
|
+
for (let unitIndex = 0; unitIndex < units.length; unitIndex += 1) {
|
|
1049
|
+
const unit = units[unitIndex];
|
|
1050
|
+
if (unit.kind === 'char') {
|
|
1051
|
+
unitIndexByRunOffset.set(`${unit.runIndex}:${unit.offset}`, unitIndex);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
for (let runIndex = 0; runIndex < runs.length; runIndex += 1) {
|
|
1055
|
+
const run = runs[runIndex];
|
|
1056
|
+
if (run.kind === 'break') {
|
|
1057
|
+
continue;
|
|
1058
|
+
}
|
|
1059
|
+
const tokenRe = new RegExp(PLACEHOLDER_TOKEN_PATTERN, 'g');
|
|
1060
|
+
let match;
|
|
1061
|
+
while ((match = tokenRe.exec(run.words)) !== null) {
|
|
1062
|
+
const startCodePoint = Array.from(run.words.slice(0, match.index)).length;
|
|
1063
|
+
const tokenLength = Array.from(match[0]).length;
|
|
1064
|
+
for (let offset = startCodePoint; offset < startCodePoint + tokenLength; offset += 1) {
|
|
1065
|
+
const unitIndex = unitIndexByRunOffset.get(`${runIndex}:${offset}`);
|
|
1066
|
+
if (unitIndex !== undefined) {
|
|
1067
|
+
inside[unitIndex] = true;
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
return inside;
|
|
1073
|
+
};
|
|
1074
|
+
/** First and exclusive-end indexes of the non-whitespace content, if any. */
|
|
1075
|
+
const contentBounds = (units) => {
|
|
1076
|
+
let start = 0;
|
|
1077
|
+
while (start < units.length && unitIsWhitespace(units[start])) {
|
|
1078
|
+
start += 1;
|
|
1079
|
+
}
|
|
1080
|
+
let end = units.length;
|
|
1081
|
+
while (end > start && unitIsWhitespace(units[end - 1])) {
|
|
1082
|
+
end -= 1;
|
|
1083
|
+
}
|
|
1084
|
+
return { start, end };
|
|
1085
|
+
};
|
|
1086
|
+
const hasNonWhitespace = (units, from, to) => {
|
|
1087
|
+
for (let index = from; index < to; index += 1) {
|
|
1088
|
+
if (!unitIsWhitespace(units[index])) {
|
|
1089
|
+
return true;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
return false;
|
|
1093
|
+
};
|
|
1094
|
+
/**
|
|
1095
|
+
* Visible length for budgeting: leading and trailing whitespace (including
|
|
1096
|
+
* `{br|}`) are ignored, and consecutive internal whitespace counts once,
|
|
1097
|
+
* matching how HTML shows it. Author whitespace is still kept verbatim in the
|
|
1098
|
+
* output.
|
|
1099
|
+
*/
|
|
1100
|
+
const visibleLength = (units) => {
|
|
1101
|
+
const { start, end } = contentBounds(units);
|
|
1102
|
+
let count = 0;
|
|
1103
|
+
let inWhitespace = false;
|
|
1104
|
+
for (let index = start; index < end; index += 1) {
|
|
1105
|
+
if (unitIsWhitespace(units[index])) {
|
|
1106
|
+
if (!inWhitespace) {
|
|
1107
|
+
count += 1;
|
|
1108
|
+
inWhitespace = true;
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
else {
|
|
1112
|
+
inWhitespace = false;
|
|
1113
|
+
count += 1;
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
return count;
|
|
1117
|
+
};
|
|
1118
|
+
/** Visible-character cost of a unit span, with internal whitespace collapsed. */
|
|
1119
|
+
const visibleCostOfSpan = (units, from, to) => {
|
|
1120
|
+
let count = 0;
|
|
1121
|
+
let inWhitespace = false;
|
|
1122
|
+
for (let index = from; index < to; index += 1) {
|
|
1123
|
+
if (unitIsWhitespace(units[index])) {
|
|
1124
|
+
if (!inWhitespace) {
|
|
1125
|
+
count += 1;
|
|
1126
|
+
inWhitespace = true;
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
else {
|
|
1130
|
+
inWhitespace = false;
|
|
1131
|
+
count += 1;
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
return count;
|
|
1135
|
+
};
|
|
1136
|
+
/**
|
|
1137
|
+
* Exclusive end index into `units` for the head. Leading whitespace costs
|
|
1138
|
+
* nothing and stays with the head. Keeps whole words within the budget; a
|
|
1139
|
+
* whole `{{ … }}` token is part of one unbreakable word (whitespace inside it
|
|
1140
|
+
* is never a cut point). If the first word alone is longer than `limit` and
|
|
1141
|
+
* contains no token, that word is cut at the budget (code points via
|
|
1142
|
+
* `Array.from`, never UTF-16 units); if it does contain a token, the whole
|
|
1143
|
+
* word stays with the head. Trailing whitespace at the cut stays out of the
|
|
1144
|
+
* head so the tail can start with it — including a `{br|}` at the cut.
|
|
1145
|
+
* Trailing whitespace after the last word is never part of the budget.
|
|
1146
|
+
*/
|
|
1147
|
+
const findHeadExclusiveEnd = (units, limit, insidePlaceholder) => {
|
|
1148
|
+
const isCutPointWhitespace = (index) => unitIsWhitespace(units[index]) && !insidePlaceholder[index];
|
|
1149
|
+
let index = 0;
|
|
1150
|
+
while (index < units.length && isCutPointWhitespace(index)) {
|
|
1151
|
+
index += 1;
|
|
1152
|
+
}
|
|
1153
|
+
if (index >= units.length) {
|
|
1154
|
+
return 0;
|
|
1155
|
+
}
|
|
1156
|
+
let visible = 0;
|
|
1157
|
+
let headEnd = index;
|
|
1158
|
+
let wordsIncluded = 0;
|
|
1159
|
+
while (index < units.length) {
|
|
1160
|
+
const wsStart = index;
|
|
1161
|
+
while (index < units.length && isCutPointWhitespace(index)) {
|
|
1162
|
+
index += 1;
|
|
1163
|
+
}
|
|
1164
|
+
const hadWhitespace = index > wsStart;
|
|
1165
|
+
if (index >= units.length) {
|
|
1166
|
+
break;
|
|
1167
|
+
}
|
|
1168
|
+
const wordStart = index;
|
|
1169
|
+
while (index < units.length && !isCutPointWhitespace(index)) {
|
|
1170
|
+
index += 1;
|
|
1171
|
+
}
|
|
1172
|
+
const wordEnd = index;
|
|
1173
|
+
const wordLength = visibleCostOfSpan(units, wordStart, wordEnd);
|
|
1174
|
+
// Leading whitespace was skipped above and costs nothing; only gaps between
|
|
1175
|
+
// words spend budget.
|
|
1176
|
+
const whitespaceCost = wordsIncluded > 0 && hadWhitespace ? 1 : 0;
|
|
1177
|
+
const cost = whitespaceCost + wordLength;
|
|
1178
|
+
if (visible + cost <= limit) {
|
|
1179
|
+
headEnd = wordEnd;
|
|
1180
|
+
visible += cost;
|
|
1181
|
+
wordsIncluded += 1;
|
|
1182
|
+
continue;
|
|
1183
|
+
}
|
|
1184
|
+
if (wordsIncluded === 0) {
|
|
1185
|
+
let wordContainsToken = false;
|
|
1186
|
+
for (let unitIndex = wordStart; unitIndex < wordEnd; unitIndex += 1) {
|
|
1187
|
+
if (insidePlaceholder[unitIndex]) {
|
|
1188
|
+
wordContainsToken = true;
|
|
1189
|
+
break;
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
// A token must stay whole; keeping the entire first word may leave the
|
|
1193
|
+
// tail empty, in which case the caller returns undefined.
|
|
1194
|
+
if (wordContainsToken) {
|
|
1195
|
+
return wordEnd;
|
|
1196
|
+
}
|
|
1197
|
+
return wordStart + (limit - visible);
|
|
1198
|
+
}
|
|
1199
|
+
return headEnd;
|
|
1200
|
+
}
|
|
1201
|
+
return headEnd;
|
|
1202
|
+
};
|
|
1203
|
+
/**
|
|
1204
|
+
* Rebuilds prepared runs so the head's words followed by the tail's equal the
|
|
1205
|
+
* full text. A span that straddles the cut becomes two spans with the same
|
|
1206
|
+
* marks, link, and styling.
|
|
1207
|
+
*/
|
|
1208
|
+
const splitPreparedRuns = (runs, units, headExclusiveEnd) => {
|
|
1209
|
+
const headCodePointEnd = runs.map(() => -1);
|
|
1210
|
+
const tailCodePointStart = runs.map(() => -1);
|
|
1211
|
+
const breakInHead = new Array(runs.length).fill(false);
|
|
1212
|
+
const breakInTail = new Array(runs.length).fill(false);
|
|
1213
|
+
for (let unitIndex = 0; unitIndex < units.length; unitIndex += 1) {
|
|
1214
|
+
const unit = units[unitIndex];
|
|
1215
|
+
const inHead = unitIndex < headExclusiveEnd;
|
|
1216
|
+
if (unit.kind === 'break') {
|
|
1217
|
+
if (inHead) {
|
|
1218
|
+
breakInHead[unit.runIndex] = true;
|
|
1219
|
+
}
|
|
1220
|
+
else {
|
|
1221
|
+
breakInTail[unit.runIndex] = true;
|
|
1222
|
+
}
|
|
1223
|
+
continue;
|
|
1224
|
+
}
|
|
1225
|
+
if (inHead) {
|
|
1226
|
+
headCodePointEnd[unit.runIndex] = unit.offset + 1;
|
|
1227
|
+
}
|
|
1228
|
+
else if (tailCodePointStart[unit.runIndex] === -1) {
|
|
1229
|
+
tailCodePointStart[unit.runIndex] = unit.offset;
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
const head = [];
|
|
1233
|
+
const tail = [];
|
|
1234
|
+
for (let runIndex = 0; runIndex < runs.length; runIndex += 1) {
|
|
1235
|
+
const run = runs[runIndex];
|
|
1236
|
+
if (run.kind === 'break') {
|
|
1237
|
+
if (breakInHead[runIndex]) {
|
|
1238
|
+
head.push(run);
|
|
1239
|
+
}
|
|
1240
|
+
if (breakInTail[runIndex]) {
|
|
1241
|
+
tail.push(run);
|
|
1242
|
+
}
|
|
1243
|
+
continue;
|
|
1244
|
+
}
|
|
1245
|
+
const codePoints = Array.from(run.words);
|
|
1246
|
+
const headEnd = headCodePointEnd[runIndex];
|
|
1247
|
+
const tailStart = tailCodePointStart[runIndex];
|
|
1248
|
+
if (headEnd > 0) {
|
|
1249
|
+
head.push({ ...run, words: codePoints.slice(0, headEnd).join('') });
|
|
1250
|
+
}
|
|
1251
|
+
if (tailStart >= 0) {
|
|
1252
|
+
tail.push({ ...run, words: codePoints.slice(tailStart).join('') });
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
return { head, tail };
|
|
1256
|
+
};
|
|
1257
|
+
/**
|
|
1258
|
+
* Same parsing, filling, and element nesting as `renderInlineMarkup`, but
|
|
1259
|
+
* splits the result into a visible head and a hidden tail at a character
|
|
1260
|
+
* budget — the shared building block for "show more" on r-text.
|
|
1261
|
+
*
|
|
1262
|
+
* Counts what a reader sees after markers are gone and `fillText` has run:
|
|
1263
|
+
* Unicode code points, with consecutive internal whitespace (and `{br|}`)
|
|
1264
|
+
* counting once for the budget only. Leading and trailing whitespace —
|
|
1265
|
+
* including leading and trailing `{br|}` — do not count, so a trailing space
|
|
1266
|
+
* past the limit never invents a "show more" that reveals nothing, and leading
|
|
1267
|
+
* spaces never consume the budget or leave an empty-looking head.
|
|
1268
|
+
*
|
|
1269
|
+
* A whole `{{ … }}` placeholder token is never cut through: whitespace inside
|
|
1270
|
+
* it is not a cut point, and the first-word fallback keeps a word that
|
|
1271
|
+
* contains a token rather than slicing the token. Counting is unchanged;
|
|
1272
|
+
* only where a cut may land changes. Applied whether or not a filler ran —
|
|
1273
|
+
* after filling, only leftover literal tokens remain.
|
|
1274
|
+
*
|
|
1275
|
+
* Returns `undefined` when everything fits, when `limit` is not a positive
|
|
1276
|
+
* integer, or when a cut would leave the head or the tail with no
|
|
1277
|
+
* non-whitespace character.
|
|
1278
|
+
*
|
|
1279
|
+
* The head keeps whole words that fit; if the first word is longer than
|
|
1280
|
+
* `limit` and has no token, that word is cut at the budget. Whitespace at the
|
|
1281
|
+
* cut — including a break — starts the tail, so concatenating the two sides'
|
|
1282
|
+
* text matches the full rendering exactly.
|
|
1283
|
+
*/
|
|
1284
|
+
function renderInlineMarkupSplit(text, limit, links, spanStyles, fillText) {
|
|
1285
|
+
if (!isPositiveIntegerLimit(limit)) {
|
|
1286
|
+
return undefined;
|
|
1287
|
+
}
|
|
1288
|
+
const runs = prepareInlineMarkupRuns(text, links, spanStyles, fillText);
|
|
1289
|
+
const units = flattenUnits(runs);
|
|
1290
|
+
if (visibleLength(units) <= limit) {
|
|
1291
|
+
return undefined;
|
|
1292
|
+
}
|
|
1293
|
+
const insidePlaceholder = markUnitsInsidePlaceholderTokens(runs, units);
|
|
1294
|
+
const headExclusiveEnd = findHeadExclusiveEnd(units, limit, insidePlaceholder);
|
|
1295
|
+
if (!hasNonWhitespace(units, 0, headExclusiveEnd) ||
|
|
1296
|
+
!hasNonWhitespace(units, headExclusiveEnd, units.length)) {
|
|
1297
|
+
return undefined;
|
|
1298
|
+
}
|
|
1299
|
+
return splitPreparedRuns(runs, units, headExclusiveEnd);
|
|
1300
|
+
}
|
|
1301
|
+
|
|
858
1302
|
class HtmlTagComponent {
|
|
859
1303
|
constructor(host, renderer, vcr) {
|
|
860
1304
|
this.host = host;
|
|
@@ -881,6 +1325,11 @@ class HtmlTagComponent {
|
|
|
881
1325
|
if (this.className !== undefined) {
|
|
882
1326
|
this.renderer.setAttribute(el, 'class', this.className);
|
|
883
1327
|
}
|
|
1328
|
+
if (this.attributes) {
|
|
1329
|
+
Object.entries(this.attributes).forEach(([name, value]) => {
|
|
1330
|
+
this.renderer.setAttribute(el, name, value);
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
884
1333
|
if (this.styleProps) {
|
|
885
1334
|
Object.entries(this.styleProps).forEach(([key, value]) => {
|
|
886
1335
|
if (value !== undefined && value !== null && value !== '') {
|
|
@@ -894,7 +1343,7 @@ class HtmlTagComponent {
|
|
|
894
1343
|
this.created = el;
|
|
895
1344
|
}
|
|
896
1345
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: HtmlTagComponent, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
897
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: HtmlTagComponent, isStandalone: true, selector: "rulecms-html-tag", inputs: { tag: "tag", styleProps: "styleProps", className: "className" }, host: { styleAttribute: "display: contents" }, viewQueries: [{ propertyName: "projected", first: true, predicate: ["projected"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: `
|
|
1346
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: HtmlTagComponent, isStandalone: true, selector: "rulecms-html-tag", inputs: { tag: "tag", styleProps: "styleProps", className: "className", attributes: "attributes" }, host: { styleAttribute: "display: contents" }, viewQueries: [{ propertyName: "projected", first: true, predicate: ["projected"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: `
|
|
898
1347
|
<ng-template #projected>
|
|
899
1348
|
<ng-content />
|
|
900
1349
|
</ng-template>
|
|
@@ -919,12 +1368,229 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
919
1368
|
type: Input
|
|
920
1369
|
}], className: [{
|
|
921
1370
|
type: Input
|
|
1371
|
+
}], attributes: [{
|
|
1372
|
+
type: Input
|
|
922
1373
|
}], projected: [{
|
|
923
1374
|
type: ViewChild,
|
|
924
1375
|
args: ['projected', { static: true }]
|
|
925
1376
|
}] } });
|
|
926
1377
|
|
|
1378
|
+
const alreadyWarned = new Set();
|
|
1379
|
+
/**
|
|
1380
|
+
* Development-only console warning, once per distinct message.
|
|
1381
|
+
*
|
|
1382
|
+
* Placeholders are filled on every render, and pre-fetched widgets render each
|
|
1383
|
+
* text up to three times for device variants, so an unguarded warning would
|
|
1384
|
+
* repeat for as long as the page is open. Silent in production: a wrong path is
|
|
1385
|
+
* a wiring mistake to catch while building, not noise on a customer's page.
|
|
1386
|
+
*/
|
|
1387
|
+
function warnPlaceholderOnce(message) {
|
|
1388
|
+
const nodeEnv = globalThis.process?.env
|
|
1389
|
+
?.NODE_ENV;
|
|
1390
|
+
if (nodeEnv === 'production') {
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
if (alreadyWarned.has(message)) {
|
|
1394
|
+
return;
|
|
1395
|
+
}
|
|
1396
|
+
alreadyWarned.add(message);
|
|
1397
|
+
console.warn(message);
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
/**
|
|
1401
|
+
* The key a host puts its values under in `componentProps`, for any component
|
|
1402
|
+
* whose text fills `{{ … }}` placeholders:
|
|
1403
|
+
*
|
|
1404
|
+
* ```tsx
|
|
1405
|
+
* componentProps={{ [textColumnId]: { placeholderValues: { user } } }}
|
|
1406
|
+
* ```
|
|
1407
|
+
*/
|
|
1408
|
+
const PLACEHOLDER_VALUES_PROP = 'placeholderValues';
|
|
1409
|
+
const isPlaceholderValues = (value) => typeof value === 'object' && value !== null;
|
|
1410
|
+
/**
|
|
1411
|
+
* The host's `placeholderValues` as something paths can be read from, or
|
|
1412
|
+
* `undefined` when there is nothing to read.
|
|
1413
|
+
*
|
|
1414
|
+
* Takes an object or array as it is, and a JSON string holding one, for hosts
|
|
1415
|
+
* that already have their values as JSON. Anything else — a string that is not
|
|
1416
|
+
* JSON, JSON for a bare number or string, a function — counts as no values, so
|
|
1417
|
+
* every placeholder prints its fallback or nothing. The value crosses a package
|
|
1418
|
+
* boundary untyped, so none of that may throw.
|
|
1419
|
+
*/
|
|
1420
|
+
function resolvePlaceholderValues(value) {
|
|
1421
|
+
if (value === undefined || value === null) {
|
|
1422
|
+
return undefined;
|
|
1423
|
+
}
|
|
1424
|
+
if (typeof value === 'string') {
|
|
1425
|
+
try {
|
|
1426
|
+
const parsed = JSON.parse(value);
|
|
1427
|
+
if (isPlaceholderValues(parsed)) {
|
|
1428
|
+
return parsed;
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
catch {
|
|
1432
|
+
// Not JSON at all; warned about below along with JSON that is not an object.
|
|
1433
|
+
}
|
|
1434
|
+
warnPlaceholderOnce(`RuleCMS: ${PLACEHOLDER_VALUES_PROP} is a string, but not JSON for an object or array, ` +
|
|
1435
|
+
`so {{ … }} placeholders show their fallbacks or nothing. It starts ` +
|
|
1436
|
+
`${JSON.stringify(value.slice(0, 40))}.`);
|
|
1437
|
+
return undefined;
|
|
1438
|
+
}
|
|
1439
|
+
if (isPlaceholderValues(value)) {
|
|
1440
|
+
return value;
|
|
1441
|
+
}
|
|
1442
|
+
warnPlaceholderOnce(`RuleCMS: ${PLACEHOLDER_VALUES_PROP} must be an object, an array, or a JSON string holding ` +
|
|
1443
|
+
`one, but it is a ${typeof value}, so {{ … }} placeholders show their fallbacks or nothing.`);
|
|
1444
|
+
return undefined;
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
/**
|
|
1448
|
+
* A key, then any number of `.key` or `[index]` steps, then optionally `??`
|
|
1449
|
+
* and fallback text. Spaces are allowed around the path and nowhere inside it.
|
|
1450
|
+
*
|
|
1451
|
+
* Keys are ASCII letters, digits, `_` and `-`, which covers camelCase,
|
|
1452
|
+
* snake_case and kebab-case JSON. Anything else between the braces makes the
|
|
1453
|
+
* whole token literal text, so an author who mistypes sees braces rather than
|
|
1454
|
+
* a silently empty gap.
|
|
1455
|
+
*/
|
|
1456
|
+
const EXPRESSION = /^\s*([A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+|\[\d+\])*)\s*(?:\?\?([\s\S]*))?$/;
|
|
1457
|
+
/** One step of a path that `EXPRESSION` has already accepted. */
|
|
1458
|
+
const PATH_STEP = /\[(\d+)\]|([A-Za-z0-9_-]+)/g;
|
|
1459
|
+
/**
|
|
1460
|
+
* The inside of one placeholder token — the text between `{{` and `}}` — to
|
|
1461
|
+
* the path and fallback it names, or `undefined` when it is not a placeholder
|
|
1462
|
+
* after all and should be shown exactly as written.
|
|
1463
|
+
*
|
|
1464
|
+
* The grammar is specified in `RText.specs.md` under "Placeholders". Every
|
|
1465
|
+
* port of the text component implements the same one, so a change here is a
|
|
1466
|
+
* change to all of them.
|
|
1467
|
+
*/
|
|
1468
|
+
function parsePlaceholderExpression(inner) {
|
|
1469
|
+
const match = EXPRESSION.exec(inner);
|
|
1470
|
+
if (!match) {
|
|
1471
|
+
return undefined;
|
|
1472
|
+
}
|
|
1473
|
+
const path = [];
|
|
1474
|
+
for (const [, index, key] of match[1].matchAll(PATH_STEP)) {
|
|
1475
|
+
path.push(index === undefined ? key : String(Number.parseInt(index, 10)));
|
|
1476
|
+
}
|
|
1477
|
+
return match[2] === undefined ? { path } : { path, fallback: match[2].trim() };
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
const PLACEHOLDER_TOKEN = new RegExp(PLACEHOLDER_TOKEN_PATTERN, 'g');
|
|
1481
|
+
const NOT_FOUND = { found: false };
|
|
1482
|
+
/**
|
|
1483
|
+
* Follows a path through the host's values without ever throwing.
|
|
1484
|
+
*
|
|
1485
|
+
* Only own properties count, so a path cannot wander onto a prototype —
|
|
1486
|
+
* `{{ user.constructor }}` finds nothing — and only objects and arrays are
|
|
1487
|
+
* stepped into. A getter or proxy that throws counts as not found rather than
|
|
1488
|
+
* taking the render down with it.
|
|
1489
|
+
*/
|
|
1490
|
+
const lookUpPath = (values, path) => {
|
|
1491
|
+
try {
|
|
1492
|
+
let current = values;
|
|
1493
|
+
for (const key of path) {
|
|
1494
|
+
if (typeof current !== 'object' ||
|
|
1495
|
+
current === null ||
|
|
1496
|
+
!Object.prototype.hasOwnProperty.call(current, key)) {
|
|
1497
|
+
return NOT_FOUND;
|
|
1498
|
+
}
|
|
1499
|
+
current = current[key];
|
|
1500
|
+
}
|
|
1501
|
+
return { found: true, value: current };
|
|
1502
|
+
}
|
|
1503
|
+
catch {
|
|
1504
|
+
return NOT_FOUND;
|
|
1505
|
+
}
|
|
1506
|
+
};
|
|
1507
|
+
/**
|
|
1508
|
+
* What a found value prints as, or `undefined` when it has no sensible text:
|
|
1509
|
+
* `null`, an object or array, a function, `NaN`. `0` and `false` print.
|
|
1510
|
+
*/
|
|
1511
|
+
const printValue = (value) => {
|
|
1512
|
+
switch (typeof value) {
|
|
1513
|
+
case 'string':
|
|
1514
|
+
return value;
|
|
1515
|
+
case 'number':
|
|
1516
|
+
return Number.isFinite(value) ? String(value) : undefined;
|
|
1517
|
+
case 'boolean':
|
|
1518
|
+
return String(value);
|
|
1519
|
+
default:
|
|
1520
|
+
return undefined;
|
|
1521
|
+
}
|
|
1522
|
+
};
|
|
1523
|
+
const describeValue = (value) => {
|
|
1524
|
+
if (Array.isArray(value)) {
|
|
1525
|
+
return 'an array';
|
|
1526
|
+
}
|
|
1527
|
+
if (typeof value === 'object') {
|
|
1528
|
+
return 'an object';
|
|
1529
|
+
}
|
|
1530
|
+
return typeof value === 'number' ? String(value) : `a ${typeof value}`;
|
|
1531
|
+
};
|
|
1532
|
+
/**
|
|
1533
|
+
* The text one placeholder prints from values the host did pass, warning in
|
|
1534
|
+
* development when its path finds nothing usable — usually a typo such as
|
|
1535
|
+
* `lastname` for `lastName`. A `null` is the host saying "no value", so it
|
|
1536
|
+
* falls back without a warning.
|
|
1537
|
+
*/
|
|
1538
|
+
const printFromValues = (values, expression, token) => {
|
|
1539
|
+
const lookup = lookUpPath(values, expression.path);
|
|
1540
|
+
if (!lookup.found) {
|
|
1541
|
+
warnPlaceholderOnce(`RuleCMS: ${token} found nothing in ${PLACEHOLDER_VALUES_PROP}. Paths are case-sensitive.`);
|
|
1542
|
+
return undefined;
|
|
1543
|
+
}
|
|
1544
|
+
const printed = printValue(lookup.value);
|
|
1545
|
+
if (printed === undefined && lookup.value !== null && lookup.value !== undefined) {
|
|
1546
|
+
warnPlaceholderOnce(`RuleCMS: ${token} found ${describeValue(lookup.value)} in ${PLACEHOLDER_VALUES_PROP}, ` +
|
|
1547
|
+
'which prints as nothing. Point it at a string, number or boolean.');
|
|
1548
|
+
}
|
|
1549
|
+
return printed;
|
|
1550
|
+
};
|
|
1551
|
+
/**
|
|
1552
|
+
* Replaces every `{{ … }}` placeholder in `text` with what its path finds in
|
|
1553
|
+
* `values`, as plain text.
|
|
1554
|
+
*
|
|
1555
|
+
* A placeholder whose path finds nothing printable — nothing there, `null`, an
|
|
1556
|
+
* empty string, an object — shows its `??` fallback, or nothing without one. A
|
|
1557
|
+
* token that is not a valid placeholder stays exactly as written.
|
|
1558
|
+
*
|
|
1559
|
+
* `values` is `undefined` when the host passed none, which is the normal case
|
|
1560
|
+
* wherever placeholders are not in use: every placeholder then falls back,
|
|
1561
|
+
* silently.
|
|
1562
|
+
*/
|
|
1563
|
+
function fillPlaceholders(text, values) {
|
|
1564
|
+
return text.replace(PLACEHOLDER_TOKEN, (token) => {
|
|
1565
|
+
const expression = parsePlaceholderExpression(token.slice(2, -2));
|
|
1566
|
+
if (expression === undefined) {
|
|
1567
|
+
return token;
|
|
1568
|
+
}
|
|
1569
|
+
const printed = values === undefined ? undefined : printFromValues(values, expression, token);
|
|
1570
|
+
return printed === undefined || printed === '' ? (expression.fallback ?? '') : printed;
|
|
1571
|
+
});
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
/**
|
|
1575
|
+
* Keeps the checkbox focusable and invisible. The toggle's accessible name
|
|
1576
|
+
* comes from the label's visible text, not from this input's own rendering.
|
|
1577
|
+
*/
|
|
1578
|
+
const VISUALLY_HIDDEN = {
|
|
1579
|
+
position: 'absolute',
|
|
1580
|
+
width: '1px',
|
|
1581
|
+
height: '1px',
|
|
1582
|
+
margin: '-1px',
|
|
1583
|
+
padding: '0',
|
|
1584
|
+
border: '0',
|
|
1585
|
+
overflow: 'hidden',
|
|
1586
|
+
clip: 'rect(0 0 0 0)',
|
|
1587
|
+
'clip-path': 'inset(50%)',
|
|
1588
|
+
'white-space': 'nowrap',
|
|
1589
|
+
};
|
|
927
1590
|
class RTextComponent {
|
|
1591
|
+
constructor() {
|
|
1592
|
+
this.visuallyHidden = VISUALLY_HIDDEN;
|
|
1593
|
+
}
|
|
928
1594
|
get content() {
|
|
929
1595
|
const contentVal = this.componentAttributes?.['content-text-3-resolutions'] ?? DEFAULT_CONTENT_TEXT;
|
|
930
1596
|
return typeof contentVal === 'string' ? contentVal : String(contentVal ?? '');
|
|
@@ -939,34 +1605,158 @@ class RTextComponent {
|
|
|
939
1605
|
get spanStyles() {
|
|
940
1606
|
return this.componentAttributes?.['inline-styles-3-resolutions'];
|
|
941
1607
|
}
|
|
1608
|
+
/**
|
|
1609
|
+
* How this render fills `{{ … }}` placeholders. On the composer canvas they
|
|
1610
|
+
* stay as the author typed them: the composer never passes host values, so
|
|
1611
|
+
* filling them there would only blank them out.
|
|
1612
|
+
*/
|
|
1613
|
+
get fillText() {
|
|
1614
|
+
if (this.pageViewType === 'Compose') {
|
|
1615
|
+
return undefined;
|
|
1616
|
+
}
|
|
1617
|
+
const values = resolvePlaceholderValues(this.widgetInstanceProps?.[PLACEHOLDER_VALUES_PROP]);
|
|
1618
|
+
return (words) => fillPlaceholders(words, values);
|
|
1619
|
+
}
|
|
1620
|
+
get showMoreAfter() {
|
|
1621
|
+
return resolveTextShowMoreAfter(this.componentAttributes);
|
|
1622
|
+
}
|
|
1623
|
+
get showMoreLabel() {
|
|
1624
|
+
return resolveTextShowMoreLabel(this.componentAttributes);
|
|
1625
|
+
}
|
|
1626
|
+
get showLessLabel() {
|
|
1627
|
+
return resolveTextShowLessLabel(this.componentAttributes);
|
|
1628
|
+
}
|
|
1629
|
+
get split() {
|
|
1630
|
+
const limit = this.showMoreAfter;
|
|
1631
|
+
if (limit === undefined) {
|
|
1632
|
+
return undefined;
|
|
1633
|
+
}
|
|
1634
|
+
return renderInlineMarkupSplit(this.content, limit, this.links, this.spanStyles, this.fillText);
|
|
1635
|
+
}
|
|
1636
|
+
/**
|
|
1637
|
+
* A configured limit with no enclosing tag always produces a span root —
|
|
1638
|
+
* truncated or not — so this agrees with `resolveRendersOwnElement`.
|
|
1639
|
+
*/
|
|
942
1640
|
get tag() {
|
|
943
|
-
|
|
1641
|
+
const resolved = resolveEnclosingTag(this.componentAttributes);
|
|
1642
|
+
return resolved ?? (this.showMoreAfter !== undefined ? 'span' : undefined);
|
|
1643
|
+
}
|
|
1644
|
+
get rootAttributes() {
|
|
1645
|
+
return this.split ? { 'data-rulecms-text-truncate': '' } : undefined;
|
|
944
1646
|
}
|
|
945
1647
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: RTextComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
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: `
|
|
1648
|
+
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", widgetInstanceProps: "widgetInstanceProps", pageViewType: "pageViewType" }, host: { styleAttribute: "display: contents" }, ngImport: i0, template: `
|
|
947
1649
|
@if (tag) {
|
|
948
|
-
<rulecms-html-tag
|
|
949
|
-
|
|
1650
|
+
<rulecms-html-tag
|
|
1651
|
+
[tag]="tag"
|
|
1652
|
+
[styleProps]="styleProps"
|
|
1653
|
+
[className]="className"
|
|
1654
|
+
[attributes]="rootAttributes"
|
|
1655
|
+
>
|
|
1656
|
+
<ng-container *ngTemplateOutlet="body" />
|
|
950
1657
|
</rulecms-html-tag>
|
|
951
1658
|
} @else {
|
|
952
|
-
<
|
|
1659
|
+
<ng-container *ngTemplateOutlet="body" />
|
|
953
1660
|
}
|
|
954
|
-
|
|
1661
|
+
|
|
1662
|
+
<ng-template #body>
|
|
1663
|
+
@if (split; as parts) {
|
|
1664
|
+
<rulecms-inline-markup [runs]="parts.head" />
|
|
1665
|
+
<span data-rulecms-text-ellipsis="" aria-hidden="true" hidden>…</span>
|
|
1666
|
+
<span data-rulecms-text-rest="">
|
|
1667
|
+
<rulecms-inline-markup [runs]="parts.tail" />
|
|
1668
|
+
</span>
|
|
1669
|
+
{{ ' ' }}
|
|
1670
|
+
<label data-rulecms-text-toggle="" hidden>
|
|
1671
|
+
<input
|
|
1672
|
+
type="checkbox"
|
|
1673
|
+
data-rulecms-text-toggle-input=""
|
|
1674
|
+
[ngStyle]="visuallyHidden"
|
|
1675
|
+
/>
|
|
1676
|
+
<span data-rulecms-text-show-more="">
|
|
1677
|
+
<rulecms-inline-markup
|
|
1678
|
+
[text]="showMoreLabel"
|
|
1679
|
+
[spanStyles]="spanStyles"
|
|
1680
|
+
[fillText]="fillText"
|
|
1681
|
+
/>
|
|
1682
|
+
</span>
|
|
1683
|
+
<span data-rulecms-text-show-less="">
|
|
1684
|
+
<rulecms-inline-markup
|
|
1685
|
+
[text]="showLessLabel"
|
|
1686
|
+
[spanStyles]="spanStyles"
|
|
1687
|
+
[fillText]="fillText"
|
|
1688
|
+
/>
|
|
1689
|
+
</span>
|
|
1690
|
+
</label>
|
|
1691
|
+
} @else {
|
|
1692
|
+
<rulecms-inline-markup
|
|
1693
|
+
[text]="content"
|
|
1694
|
+
[links]="links"
|
|
1695
|
+
[spanStyles]="spanStyles"
|
|
1696
|
+
[fillText]="fillText"
|
|
1697
|
+
/>
|
|
1698
|
+
}
|
|
1699
|
+
</ng-template>
|
|
1700
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: InlineMarkupComponent, selector: "rulecms-inline-markup", inputs: ["text", "links", "spanStyles", "fillText", "runs"] }, { kind: "component", type: HtmlTagComponent, selector: "rulecms-html-tag", inputs: ["tag", "styleProps", "className", "attributes"] }] }); }
|
|
955
1701
|
}
|
|
956
1702
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: RTextComponent, decorators: [{
|
|
957
1703
|
type: Component,
|
|
958
1704
|
args: [{
|
|
959
1705
|
selector: 'rulecms-r-text',
|
|
960
1706
|
standalone: true,
|
|
961
|
-
imports: [InlineMarkupComponent, HtmlTagComponent],
|
|
1707
|
+
imports: [NgStyle, NgTemplateOutlet, InlineMarkupComponent, HtmlTagComponent],
|
|
962
1708
|
template: `
|
|
963
1709
|
@if (tag) {
|
|
964
|
-
<rulecms-html-tag
|
|
965
|
-
|
|
1710
|
+
<rulecms-html-tag
|
|
1711
|
+
[tag]="tag"
|
|
1712
|
+
[styleProps]="styleProps"
|
|
1713
|
+
[className]="className"
|
|
1714
|
+
[attributes]="rootAttributes"
|
|
1715
|
+
>
|
|
1716
|
+
<ng-container *ngTemplateOutlet="body" />
|
|
966
1717
|
</rulecms-html-tag>
|
|
967
1718
|
} @else {
|
|
968
|
-
<
|
|
1719
|
+
<ng-container *ngTemplateOutlet="body" />
|
|
969
1720
|
}
|
|
1721
|
+
|
|
1722
|
+
<ng-template #body>
|
|
1723
|
+
@if (split; as parts) {
|
|
1724
|
+
<rulecms-inline-markup [runs]="parts.head" />
|
|
1725
|
+
<span data-rulecms-text-ellipsis="" aria-hidden="true" hidden>…</span>
|
|
1726
|
+
<span data-rulecms-text-rest="">
|
|
1727
|
+
<rulecms-inline-markup [runs]="parts.tail" />
|
|
1728
|
+
</span>
|
|
1729
|
+
{{ ' ' }}
|
|
1730
|
+
<label data-rulecms-text-toggle="" hidden>
|
|
1731
|
+
<input
|
|
1732
|
+
type="checkbox"
|
|
1733
|
+
data-rulecms-text-toggle-input=""
|
|
1734
|
+
[ngStyle]="visuallyHidden"
|
|
1735
|
+
/>
|
|
1736
|
+
<span data-rulecms-text-show-more="">
|
|
1737
|
+
<rulecms-inline-markup
|
|
1738
|
+
[text]="showMoreLabel"
|
|
1739
|
+
[spanStyles]="spanStyles"
|
|
1740
|
+
[fillText]="fillText"
|
|
1741
|
+
/>
|
|
1742
|
+
</span>
|
|
1743
|
+
<span data-rulecms-text-show-less="">
|
|
1744
|
+
<rulecms-inline-markup
|
|
1745
|
+
[text]="showLessLabel"
|
|
1746
|
+
[spanStyles]="spanStyles"
|
|
1747
|
+
[fillText]="fillText"
|
|
1748
|
+
/>
|
|
1749
|
+
</span>
|
|
1750
|
+
</label>
|
|
1751
|
+
} @else {
|
|
1752
|
+
<rulecms-inline-markup
|
|
1753
|
+
[text]="content"
|
|
1754
|
+
[links]="links"
|
|
1755
|
+
[spanStyles]="spanStyles"
|
|
1756
|
+
[fillText]="fillText"
|
|
1757
|
+
/>
|
|
1758
|
+
}
|
|
1759
|
+
</ng-template>
|
|
970
1760
|
`,
|
|
971
1761
|
host: { style: 'display: contents' },
|
|
972
1762
|
}]
|
|
@@ -976,6 +1766,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
976
1766
|
type: Input
|
|
977
1767
|
}], className: [{
|
|
978
1768
|
type: Input
|
|
1769
|
+
}], widgetInstanceProps: [{
|
|
1770
|
+
type: Input
|
|
1771
|
+
}], pageViewType: [{
|
|
1772
|
+
type: Input
|
|
979
1773
|
}] } });
|
|
980
1774
|
|
|
981
1775
|
/**
|
|
@@ -992,13 +1786,17 @@ const resolveNoContainer$9 = ({ componentAttributes, }) => !isBlockEnclosingTag(
|
|
|
992
1786
|
|
|
993
1787
|
/**
|
|
994
1788
|
* True when RText is about to render an element that can carry a className —
|
|
995
|
-
* any enclosing tag, block or inline
|
|
1789
|
+
* any enclosing tag, block or inline, or a bare text with a show-more limit
|
|
1790
|
+
* (which renders as a `span`). True bare text has no element, so false.
|
|
996
1791
|
*
|
|
997
|
-
* Shares `resolveEnclosingTag` with RText and
|
|
998
|
-
* disagreed with what the component renders, a
|
|
999
|
-
* nothing or force a redundant wrapper.
|
|
1792
|
+
* Shares `resolveEnclosingTag` and `resolveTextShowMoreAfter` with RText and
|
|
1793
|
+
* `resolveNoContainer`: if this disagreed with what the component renders, a
|
|
1794
|
+
* class would either land on nothing or force a redundant wrapper. The limit
|
|
1795
|
+
* is part of the answer because a configured budget always produces a root
|
|
1796
|
+
* element, even when the filled text happens to fit.
|
|
1000
1797
|
*/
|
|
1001
|
-
const resolveRendersOwnElement$9 = ({ componentAttributes, }) => !!resolveEnclosingTag(componentAttributes)
|
|
1798
|
+
const resolveRendersOwnElement$9 = ({ componentAttributes, }) => !!resolveEnclosingTag(componentAttributes) ||
|
|
1799
|
+
resolveTextShowMoreAfter(componentAttributes) !== undefined;
|
|
1002
1800
|
|
|
1003
1801
|
const attributeEditModeConfigs$9 = [
|
|
1004
1802
|
{
|
|
@@ -1023,6 +1821,24 @@ const attributeEditModeConfigs$9 = [
|
|
|
1023
1821
|
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
1822
|
defaultValues: {},
|
|
1025
1823
|
},
|
|
1824
|
+
{
|
|
1825
|
+
name: TEXT_SHOW_MORE_AFTER_ATTRIBUTE,
|
|
1826
|
+
heading: 'Show More After',
|
|
1827
|
+
subHeading: 'How many characters a reader sees before the rest is hidden behind Show more. The count is the characters they read — markers and fillers already applied — and the cut ends on a whole word; leave empty to always show all of it, for 3 different device resolutions',
|
|
1828
|
+
defaultValues: {},
|
|
1829
|
+
},
|
|
1830
|
+
{
|
|
1831
|
+
name: TEXT_SHOW_MORE_LABEL_ATTRIBUTE,
|
|
1832
|
+
heading: 'Show More Label',
|
|
1833
|
+
subHeading: 'The words on the toggle while the text is collapsed. Supports the {b|…}, {i|…} and {.1|…} markers against this text\'s Class Names & Styles list, but not links; leave empty for "Show more", for 3 different device resolutions',
|
|
1834
|
+
defaultValues: {},
|
|
1835
|
+
},
|
|
1836
|
+
{
|
|
1837
|
+
name: TEXT_SHOW_LESS_LABEL_ATTRIBUTE,
|
|
1838
|
+
heading: 'Show Less Label',
|
|
1839
|
+
subHeading: 'The words on the toggle while the text is expanded. Supports the {b|…}, {i|…} and {.1|…} markers against this text\'s Class Names & Styles list, but not links; leave empty for "Show less", for 3 different device resolutions',
|
|
1840
|
+
defaultValues: {},
|
|
1841
|
+
},
|
|
1026
1842
|
];
|
|
1027
1843
|
|
|
1028
1844
|
const ComponentIdentifiers = {
|
|
@@ -2541,12 +3357,13 @@ class RButtonComponent {
|
|
|
2541
3357
|
onClick?.(event);
|
|
2542
3358
|
}
|
|
2543
3359
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: RButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2544
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.25", type: RButtonComponent, isStandalone: true, selector: "rulecms-r-button", inputs: { componentAttributes: "componentAttributes", styleProps: "styleProps", className: "className", widgetInstanceProps: "widgetInstanceProps" }, host: { styleAttribute: "display: contents" }, ngImport: i0, template: `
|
|
3360
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.25", type: RButtonComponent, isStandalone: true, selector: "rulecms-r-button", inputs: { componentAttributes: "componentAttributes", styleProps: "styleProps", className: "className", widgetInstanceProps: "widgetInstanceProps", columnPath: "columnPath" }, host: { styleAttribute: "display: contents" }, ngImport: i0, template: `
|
|
2545
3361
|
@if (link && disabled) {
|
|
2546
3362
|
<a
|
|
2547
3363
|
data-rulecms-button
|
|
2548
3364
|
[attr.data-rulecms-button-variant]="preset.variant"
|
|
2549
3365
|
[attr.data-rulecms-button-tone]="preset.tone"
|
|
3366
|
+
[attr.data-rulecms-column-path]="columnPath"
|
|
2550
3367
|
[ngStyle]="style"
|
|
2551
3368
|
[class]="className || undefined"
|
|
2552
3369
|
role="link"
|
|
@@ -2566,6 +3383,7 @@ class RButtonComponent {
|
|
|
2566
3383
|
data-rulecms-button
|
|
2567
3384
|
[attr.data-rulecms-button-variant]="preset.variant"
|
|
2568
3385
|
[attr.data-rulecms-button-tone]="preset.tone"
|
|
3386
|
+
[attr.data-rulecms-column-path]="columnPath"
|
|
2569
3387
|
[attr.href]="link.href"
|
|
2570
3388
|
[attr.target]="link.target"
|
|
2571
3389
|
[attr.rel]="link.rel"
|
|
@@ -2592,6 +3410,7 @@ class RButtonComponent {
|
|
|
2592
3410
|
data-rulecms-button
|
|
2593
3411
|
[attr.data-rulecms-button-variant]="preset.variant"
|
|
2594
3412
|
[attr.data-rulecms-button-tone]="preset.tone"
|
|
3413
|
+
[attr.data-rulecms-column-path]="columnPath"
|
|
2595
3414
|
[disabled]="disabled"
|
|
2596
3415
|
[ngStyle]="style"
|
|
2597
3416
|
[class]="className || undefined"
|
|
@@ -2621,6 +3440,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
2621
3440
|
data-rulecms-button
|
|
2622
3441
|
[attr.data-rulecms-button-variant]="preset.variant"
|
|
2623
3442
|
[attr.data-rulecms-button-tone]="preset.tone"
|
|
3443
|
+
[attr.data-rulecms-column-path]="columnPath"
|
|
2624
3444
|
[ngStyle]="style"
|
|
2625
3445
|
[class]="className || undefined"
|
|
2626
3446
|
role="link"
|
|
@@ -2640,6 +3460,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
2640
3460
|
data-rulecms-button
|
|
2641
3461
|
[attr.data-rulecms-button-variant]="preset.variant"
|
|
2642
3462
|
[attr.data-rulecms-button-tone]="preset.tone"
|
|
3463
|
+
[attr.data-rulecms-column-path]="columnPath"
|
|
2643
3464
|
[attr.href]="link.href"
|
|
2644
3465
|
[attr.target]="link.target"
|
|
2645
3466
|
[attr.rel]="link.rel"
|
|
@@ -2666,6 +3487,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
2666
3487
|
data-rulecms-button
|
|
2667
3488
|
[attr.data-rulecms-button-variant]="preset.variant"
|
|
2668
3489
|
[attr.data-rulecms-button-tone]="preset.tone"
|
|
3490
|
+
[attr.data-rulecms-column-path]="columnPath"
|
|
2669
3491
|
[disabled]="disabled"
|
|
2670
3492
|
[ngStyle]="style"
|
|
2671
3493
|
[class]="className || undefined"
|
|
@@ -2692,6 +3514,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
2692
3514
|
type: Input
|
|
2693
3515
|
}], widgetInstanceProps: [{
|
|
2694
3516
|
type: Input
|
|
3517
|
+
}], columnPath: [{
|
|
3518
|
+
type: Input
|
|
2695
3519
|
}] } });
|
|
2696
3520
|
|
|
2697
3521
|
/**
|
|
@@ -3655,7 +4479,7 @@ class RListComponent {
|
|
|
3655
4479
|
}
|
|
3656
4480
|
</ul>
|
|
3657
4481
|
}
|
|
3658
|
-
`, isInline: true, dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: InlineMarkupComponent, selector: "rulecms-inline-markup", inputs: ["text", "links", "spanStyles"] }] }); }
|
|
4482
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: InlineMarkupComponent, selector: "rulecms-inline-markup", inputs: ["text", "links", "spanStyles", "fillText", "runs"] }] }); }
|
|
3659
4483
|
}
|
|
3660
4484
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: RListComponent, decorators: [{
|
|
3661
4485
|
type: Component,
|
|
@@ -3865,7 +4689,7 @@ const DEFAULT_ACCORDION_HEADING_LEVEL = 'none';
|
|
|
3865
4689
|
/**
|
|
3866
4690
|
* The summary, on one line. An unset attribute falls back to the placeholder,
|
|
3867
4691
|
* but text the author deliberately cleared stays cleared: an empty `<summary>`
|
|
3868
|
-
* still draws its
|
|
4692
|
+
* still draws its chevron and still toggles, and the composer needs the element
|
|
3869
4693
|
* to stay selectable on the canvas. Same distinction `r-list` draws for its
|
|
3870
4694
|
* items and `r-button` for its label.
|
|
3871
4695
|
*
|
|
@@ -3947,18 +4771,29 @@ const resolveAccordionHeadingLevel = (componentAttributes) => {
|
|
|
3947
4771
|
};
|
|
3948
4772
|
|
|
3949
4773
|
const SUMMARY_LAYOUT_STYLE = {
|
|
3950
|
-
display: '
|
|
4774
|
+
display: 'flex',
|
|
4775
|
+
alignItems: 'center',
|
|
3951
4776
|
width: '100%',
|
|
3952
4777
|
boxSizing: 'border-box',
|
|
4778
|
+
cursor: 'pointer',
|
|
4779
|
+
listStyle: 'none',
|
|
3953
4780
|
};
|
|
3954
4781
|
const SUMMARY_HEADING_STYLE = {
|
|
3955
|
-
|
|
4782
|
+
flex: '1',
|
|
4783
|
+
minWidth: 0,
|
|
3956
4784
|
margin: 0,
|
|
3957
4785
|
};
|
|
4786
|
+
const CHEVRON_STYLE = {
|
|
4787
|
+
width: '20px',
|
|
4788
|
+
height: '20px',
|
|
4789
|
+
flexShrink: 0,
|
|
4790
|
+
marginLeft: 'auto',
|
|
4791
|
+
};
|
|
3958
4792
|
class RAccordionComponent {
|
|
3959
4793
|
constructor() {
|
|
3960
4794
|
this.summaryLayoutStyle = SUMMARY_LAYOUT_STYLE;
|
|
3961
4795
|
this.summaryHeadingStyle = SUMMARY_HEADING_STYLE;
|
|
4796
|
+
this.chevronStyle = CHEVRON_STYLE;
|
|
3962
4797
|
}
|
|
3963
4798
|
get summary() {
|
|
3964
4799
|
return resolveAccordionSummary(this.componentAttributes);
|
|
@@ -3980,7 +4815,12 @@ class RAccordionComponent {
|
|
|
3980
4815
|
}
|
|
3981
4816
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: RAccordionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3982
4817
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.25", type: RAccordionComponent, isStandalone: true, selector: "rulecms-r-accordion", inputs: { componentAttributes: "componentAttributes", styleProps: "styleProps", className: "className" }, host: { styleAttribute: "display: contents" }, ngImport: i0, template: `
|
|
3983
|
-
<details
|
|
4818
|
+
<details
|
|
4819
|
+
data-rulecms-accordion
|
|
4820
|
+
[ngStyle]="styleProps"
|
|
4821
|
+
[class]="className || undefined"
|
|
4822
|
+
[attr.open]="open ? true : null"
|
|
4823
|
+
>
|
|
3984
4824
|
<summary [ngStyle]="summaryLayoutStyle">
|
|
3985
4825
|
@if (headingLevel) {
|
|
3986
4826
|
<rulecms-html-tag [tag]="headingLevel" [styleProps]="summaryHeadingStyle">
|
|
@@ -3989,6 +4829,22 @@ class RAccordionComponent {
|
|
|
3989
4829
|
} @else {
|
|
3990
4830
|
{{ summary }}
|
|
3991
4831
|
}
|
|
4832
|
+
<svg
|
|
4833
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
4834
|
+
width="24"
|
|
4835
|
+
height="24"
|
|
4836
|
+
viewBox="0 0 24 24"
|
|
4837
|
+
fill="none"
|
|
4838
|
+
stroke="currentColor"
|
|
4839
|
+
stroke-width="2"
|
|
4840
|
+
stroke-linecap="round"
|
|
4841
|
+
stroke-linejoin="round"
|
|
4842
|
+
data-rulecms-accordion-chevron
|
|
4843
|
+
aria-hidden="true"
|
|
4844
|
+
[ngStyle]="chevronStyle"
|
|
4845
|
+
>
|
|
4846
|
+
<path d="m6 9 6 6 6-6" />
|
|
4847
|
+
</svg>
|
|
3992
4848
|
</summary>
|
|
3993
4849
|
@for (paragraph of detail; track $index) {
|
|
3994
4850
|
<p>
|
|
@@ -3997,7 +4853,7 @@ class RAccordionComponent {
|
|
|
3997
4853
|
}
|
|
3998
4854
|
<ng-content />
|
|
3999
4855
|
</details>
|
|
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"] }] }); }
|
|
4856
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: InlineMarkupComponent, selector: "rulecms-inline-markup", inputs: ["text", "links", "spanStyles", "fillText", "runs"] }, { kind: "component", type: HtmlTagComponent, selector: "rulecms-html-tag", inputs: ["tag", "styleProps", "className", "attributes"] }] }); }
|
|
4001
4857
|
}
|
|
4002
4858
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: RAccordionComponent, decorators: [{
|
|
4003
4859
|
type: Component,
|
|
@@ -4006,7 +4862,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
4006
4862
|
standalone: true,
|
|
4007
4863
|
imports: [NgStyle, InlineMarkupComponent, HtmlTagComponent],
|
|
4008
4864
|
template: `
|
|
4009
|
-
<details
|
|
4865
|
+
<details
|
|
4866
|
+
data-rulecms-accordion
|
|
4867
|
+
[ngStyle]="styleProps"
|
|
4868
|
+
[class]="className || undefined"
|
|
4869
|
+
[attr.open]="open ? true : null"
|
|
4870
|
+
>
|
|
4010
4871
|
<summary [ngStyle]="summaryLayoutStyle">
|
|
4011
4872
|
@if (headingLevel) {
|
|
4012
4873
|
<rulecms-html-tag [tag]="headingLevel" [styleProps]="summaryHeadingStyle">
|
|
@@ -4015,6 +4876,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
4015
4876
|
} @else {
|
|
4016
4877
|
{{ summary }}
|
|
4017
4878
|
}
|
|
4879
|
+
<svg
|
|
4880
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
4881
|
+
width="24"
|
|
4882
|
+
height="24"
|
|
4883
|
+
viewBox="0 0 24 24"
|
|
4884
|
+
fill="none"
|
|
4885
|
+
stroke="currentColor"
|
|
4886
|
+
stroke-width="2"
|
|
4887
|
+
stroke-linecap="round"
|
|
4888
|
+
stroke-linejoin="round"
|
|
4889
|
+
data-rulecms-accordion-chevron
|
|
4890
|
+
aria-hidden="true"
|
|
4891
|
+
[ngStyle]="chevronStyle"
|
|
4892
|
+
>
|
|
4893
|
+
<path d="m6 9 6 6 6-6" />
|
|
4894
|
+
</svg>
|
|
4018
4895
|
</summary>
|
|
4019
4896
|
@for (paragraph of detail; track $index) {
|
|
4020
4897
|
<p>
|
|
@@ -4497,6 +5374,12 @@ const componentNameToModifyAttribute = {
|
|
|
4497
5374
|
'enclosing-tag-3-resolutions': 'enclosing-tag-3-resolutions',
|
|
4498
5375
|
'links-3-resolutions': 'links-3-resolutions',
|
|
4499
5376
|
'inline-styles-3-resolutions': 'inline-styles-3-resolutions',
|
|
5377
|
+
// r-text's show-more budget is a character count, not a length in pixels,
|
|
5378
|
+
// so it reuses the plain-text field the same way list-start does. The two
|
|
5379
|
+
// toggle labels are free prose and reuse the multi-line text field.
|
|
5380
|
+
'text-show-more-after-3-resolutions': 'image-text-3-resolutions',
|
|
5381
|
+
'text-show-more-label-3-resolutions': 'content-text-3-resolutions',
|
|
5382
|
+
'text-show-less-label-3-resolutions': 'content-text-3-resolutions',
|
|
4500
5383
|
// r-list owns its own attribute names — one component per documented
|
|
4501
5384
|
// setting — but reuses existing editors wherever one fits: the multi-line
|
|
4502
5385
|
// text field for its items, the numbered link list for its links, and the
|
|
@@ -4641,6 +5524,54 @@ const getPreviewComponents = () => {
|
|
|
4641
5524
|
return cachedPreviewComponents;
|
|
4642
5525
|
};
|
|
4643
5526
|
|
|
5527
|
+
/** Published package version. `npm version` keeps this in sync with package.json. */
|
|
5528
|
+
const libraryVersion = '0.5.0';
|
|
5529
|
+
|
|
5530
|
+
/**
|
|
5531
|
+
* SSR-safe rules for `r-accordion` motion and the custom chevron.
|
|
5532
|
+
*
|
|
5533
|
+
* widget-angular injects this once per library via `ComponentLibraryModule.stylesheet`.
|
|
5534
|
+
* Open/close height and opacity cannot be inline styles: they key off `[open]`
|
|
5535
|
+
* and `::details-content`. The chevron rotate is the same. Hiding the UA
|
|
5536
|
+
* triangle belongs here too — `summary::marker` is a pseudo-element.
|
|
5537
|
+
*
|
|
5538
|
+
* Browsers that do not support `::details-content` / `interpolate-size` still
|
|
5539
|
+
* open and close; they just snap. The chevron and the disclosure keep working.
|
|
5540
|
+
*/
|
|
5541
|
+
const ACCORDION_LIBRARY_STYLESHEET = `
|
|
5542
|
+
[data-rulecms-accordion] {
|
|
5543
|
+
interpolate-size: allow-keywords;
|
|
5544
|
+
}
|
|
5545
|
+
[data-rulecms-accordion] > summary {
|
|
5546
|
+
list-style: none;
|
|
5547
|
+
}
|
|
5548
|
+
[data-rulecms-accordion] > summary::-webkit-details-marker {
|
|
5549
|
+
display: none;
|
|
5550
|
+
}
|
|
5551
|
+
[data-rulecms-accordion] > summary::marker {
|
|
5552
|
+
content: none;
|
|
5553
|
+
}
|
|
5554
|
+
[data-rulecms-accordion]::details-content {
|
|
5555
|
+
overflow: hidden;
|
|
5556
|
+
opacity: 0;
|
|
5557
|
+
block-size: 0;
|
|
5558
|
+
transition:
|
|
5559
|
+
block-size 300ms ease,
|
|
5560
|
+
opacity 300ms ease,
|
|
5561
|
+
content-visibility 300ms allow-discrete;
|
|
5562
|
+
}
|
|
5563
|
+
[data-rulecms-accordion][open]::details-content {
|
|
5564
|
+
opacity: 1;
|
|
5565
|
+
block-size: auto;
|
|
5566
|
+
}
|
|
5567
|
+
[data-rulecms-accordion-chevron] {
|
|
5568
|
+
transition: transform 300ms ease;
|
|
5569
|
+
}
|
|
5570
|
+
[data-rulecms-accordion][open] [data-rulecms-accordion-chevron] {
|
|
5571
|
+
transform: rotate(180deg);
|
|
5572
|
+
}
|
|
5573
|
+
`.trim();
|
|
5574
|
+
|
|
4644
5575
|
/**
|
|
4645
5576
|
* SSR-safe rules for `r-button` pseudo-classes.
|
|
4646
5577
|
*
|
|
@@ -4684,6 +5615,57 @@ const BUTTON_LIBRARY_STYLESHEET = `
|
|
|
4684
5615
|
}
|
|
4685
5616
|
`.trim();
|
|
4686
5617
|
|
|
5618
|
+
/**
|
|
5619
|
+
* SSR-safe rules for `r-text` show more / show less.
|
|
5620
|
+
*
|
|
5621
|
+
* widget-angular injects this once per library via `ComponentLibraryModule.stylesheet`.
|
|
5622
|
+
* Collapsing cannot be an inline style: it keys off whether a checkbox inside
|
|
5623
|
+
* the toggle is checked, which only CSS `:has()` can see without JavaScript.
|
|
5624
|
+
*
|
|
5625
|
+
* Every rule sits inside `@supports selector(:has(*))` and is scoped to
|
|
5626
|
+
* `[data-rulecms-text-truncate]` with child combinators. Without that support
|
|
5627
|
+
* — or without this sheet — the markup's `hidden` attributes leave the full
|
|
5628
|
+
* text visible and the toggle invisible, which is the progressive-enhancement
|
|
5629
|
+
* path on purpose.
|
|
5630
|
+
*/
|
|
5631
|
+
const TEXT_LIBRARY_STYLESHEET = `
|
|
5632
|
+
@supports selector(:has(*)) {
|
|
5633
|
+
[data-rulecms-text-truncate] > [data-rulecms-text-toggle] {
|
|
5634
|
+
display: inline;
|
|
5635
|
+
cursor: pointer;
|
|
5636
|
+
color: inherit;
|
|
5637
|
+
font: inherit;
|
|
5638
|
+
}
|
|
5639
|
+
[data-rulecms-text-truncate] > [data-rulecms-text-toggle]:has(> [data-rulecms-text-toggle-input]:focus-visible) {
|
|
5640
|
+
outline: 2px solid currentColor;
|
|
5641
|
+
outline-offset: 2px;
|
|
5642
|
+
}
|
|
5643
|
+
[data-rulecms-text-truncate] > [data-rulecms-text-toggle] > [data-rulecms-text-show-more]:not(:has(*)),
|
|
5644
|
+
[data-rulecms-text-truncate] > [data-rulecms-text-toggle] > [data-rulecms-text-show-less]:not(:has(*)) {
|
|
5645
|
+
text-decoration: underline;
|
|
5646
|
+
}
|
|
5647
|
+
[data-rulecms-text-truncate]:not(:has(> [data-rulecms-text-toggle] > [data-rulecms-text-toggle-input]:checked)) > [data-rulecms-text-ellipsis] {
|
|
5648
|
+
display: inline;
|
|
5649
|
+
}
|
|
5650
|
+
[data-rulecms-text-truncate]:not(:has(> [data-rulecms-text-toggle] > [data-rulecms-text-toggle-input]:checked)) > [data-rulecms-text-rest] {
|
|
5651
|
+
display: none;
|
|
5652
|
+
}
|
|
5653
|
+
[data-rulecms-text-truncate]:not(:has(> [data-rulecms-text-toggle] > [data-rulecms-text-toggle-input]:checked)) > [data-rulecms-text-toggle] > [data-rulecms-text-show-less] {
|
|
5654
|
+
display: none;
|
|
5655
|
+
}
|
|
5656
|
+
[data-rulecms-text-truncate]:has(> [data-rulecms-text-toggle] > [data-rulecms-text-toggle-input]:checked) > [data-rulecms-text-toggle] > [data-rulecms-text-show-more] {
|
|
5657
|
+
display: none;
|
|
5658
|
+
}
|
|
5659
|
+
}
|
|
5660
|
+
`.trim();
|
|
5661
|
+
|
|
5662
|
+
/**
|
|
5663
|
+
* Optional `ComponentLibraryModule.stylesheet`. widget-angular injects this
|
|
5664
|
+
* once per library as an SSR `<style>` tag. Button hover / focus / active,
|
|
5665
|
+
* accordion motion, and text show-more rules are concatenated here.
|
|
5666
|
+
*/
|
|
5667
|
+
const stylesheet = `${BUTTON_LIBRARY_STYLESHEET}\n${ACCORDION_LIBRARY_STYLESHEET}\n${TEXT_LIBRARY_STYLESHEET}`;
|
|
5668
|
+
|
|
4687
5669
|
/**
|
|
4688
5670
|
* ComponentLibraryModule conformance (the @rulecms/widget-angular contract).
|
|
4689
5671
|
*
|
|
@@ -4697,17 +5679,11 @@ const BUTTON_LIBRARY_STYLESHEET = `
|
|
|
4697
5679
|
*/
|
|
4698
5680
|
const manifest = {
|
|
4699
5681
|
id: 'default',
|
|
4700
|
-
version:
|
|
5682
|
+
version: libraryVersion,
|
|
4701
5683
|
angularPeerRange: '>=17.0.0',
|
|
4702
5684
|
};
|
|
4703
5685
|
/** The render surface: component type → renderable entry. */
|
|
4704
5686
|
const components = getSourceComponentsPlugin().components;
|
|
4705
|
-
/**
|
|
4706
|
-
* Optional `ComponentLibraryModule.stylesheet`. widget-angular injects this
|
|
4707
|
-
* once per library as an SSR `<style>` tag. v1 is the button hover / focus /
|
|
4708
|
-
* active rules; later interactive cards append here.
|
|
4709
|
-
*/
|
|
4710
|
-
const stylesheet = BUTTON_LIBRARY_STYLESHEET;
|
|
4711
5687
|
/** The editor surface: palette + attribute metadata for the RuleCMS composer. */
|
|
4712
5688
|
const editor = {
|
|
4713
5689
|
previewComponents: getPreviewComponents(),
|
|
@@ -4770,5 +5746,5 @@ function serializeInlineMarkers(runs) {
|
|
|
4770
5746
|
* Generated bundle index. Do not edit.
|
|
4771
5747
|
*/
|
|
4772
5748
|
|
|
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 };
|
|
5749
|
+
export { ACCORDION_HEADING_LEVELS, ACCORDION_LIBRARY_STYLESHEET, 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, PLACEHOLDER_VALUES_PROP, REJECTED_HREF_SCHEMES, SIZE_SCALES, TEXT_LIBRARY_STYLESHEET, TONE_PALETTES, applyButtonColumnStyles, buildButtonPresetStyles, buildButtonRenderStyles, disabledStyles as buttonDisabledStyles, cardGroups$1 as cardGroups, components, editor, fillPlaceholders, formatInlineMarker, formatStyleDeclarations, getComponentMetadataForEditing, getPreviewCardGroups, getPreviewComponents, getSourceComponentsPlugin, getSpanStyleItemAt, isAllowedEmbedUrl, isAllowedMountName, isBlockEnclosingTag, isButtonSize, isButtonTone, isButtonVariant, isEmptySpanStyleItem, isEnclosingTag, isForbiddenMountPropKey, isSafeHref, isSpanStyleItem, manifest, normaliseHost, parseInlineLinkMarkers, parseInlineMarkers, parseStyleDeclarations, renderInlineMarkupSplit, resolveButtonPreset, resolveClassNames, resolveEmbedUrl, resolveLinkAttributes, resolveMountName, resolveMountProps, resolvePlaceholderValues, resolveSpanStyleAttributes, serializeInlineMarkers, stylesheet, toneCssVariable };
|
|
4774
5750
|
//# sourceMappingURL=rulecms-source-components-angular.mjs.map
|