@aglyn/shared-data-enums 1.0.0-beta.143
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/LICENSE +201 -0
- package/README.md +3 -0
- package/package.json +39 -0
- package/src/enums.d.ts +26 -0
- package/src/enums.js +27 -0
- package/src/enums.js.map +1 -0
- package/src/index.d.ts +17 -0
- package/src/index.js +18 -0
- package/src/index.js.map +1 -0
- package/src/lib/aglyn-applications.d.ts +39 -0
- package/src/lib/aglyn-applications.js +53 -0
- package/src/lib/aglyn-applications.js.map +1 -0
- package/src/lib/aglyn.d.ts +49 -0
- package/src/lib/aglyn.js +86 -0
- package/src/lib/aglyn.js.map +1 -0
- package/src/lib/breakpoint-span.d.ts +66 -0
- package/src/lib/breakpoint-span.js +117 -0
- package/src/lib/breakpoint-span.js.map +1 -0
- package/src/lib/data-table.d.ts +107 -0
- package/src/lib/data-table.js +225 -0
- package/src/lib/data-table.js.map +1 -0
- package/src/lib/firebase-auth.d.ts +73 -0
- package/src/lib/firebase-auth.js +178 -0
- package/src/lib/firebase-auth.js.map +1 -0
- package/src/lib/global.d.ts +32 -0
- package/src/lib/global.js +33 -0
- package/src/lib/global.js.map +1 -0
- package/src/lib/http.d.ts +145 -0
- package/src/lib/http.js +155 -0
- package/src/lib/http.js.map +1 -0
- package/src/lib/icons.d.ts +108 -0
- package/src/lib/icons.js +130 -0
- package/src/lib/icons.js.map +1 -0
- package/src/lib/palette-token-css-var.d.ts +111 -0
- package/src/lib/palette-token-css-var.js +221 -0
- package/src/lib/palette-token-css-var.js.map +1 -0
- package/src/lib/styles.d.ts +170 -0
- package/src/lib/styles.js +315 -0
- package/src/lib/styles.js.map +1 -0
- package/src/lib/sx-property-aliases.d.ts +129 -0
- package/src/lib/sx-property-aliases.js +231 -0
- package/src/lib/sx-property-aliases.js.map +1 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* The responsive span/offset syntax the besigner persists for MUI Grid
|
|
19
|
+
* (AGL-2486).
|
|
20
|
+
*
|
|
21
|
+
* A Grid cell's `size` and `offset` are stored as ONE string in the node's
|
|
22
|
+
* props — `"6"`, `"auto"`, `"xs:12 md:6"` — because MUI v6+ replaced the
|
|
23
|
+
* per-breakpoint `xs=`/`md=` props with a single value-or-object prop. That
|
|
24
|
+
* string is the persisted format and it does not change here; what changes is
|
|
25
|
+
* that the attributes panel no longer asks an author to TYPE it (a developer
|
|
26
|
+
* syntax nobody outside the docs knows). Parse and serialize live together in
|
|
27
|
+
* this shared module, the same arrangement `parseCssDimension`/
|
|
28
|
+
* `buildCssDimension` above have, so the editor and the renderer cannot drift
|
|
29
|
+
* into two different readings of the same stored string.
|
|
30
|
+
*
|
|
31
|
+
* `raw` is the escape hatch that keeps the editor from destroying what it
|
|
32
|
+
* cannot model — a half-typed pair, a `{{token}}` binding, an unknown
|
|
33
|
+
* breakpoint. The string comes back untouched instead of collapsing to
|
|
34
|
+
* whatever a lenient parse salvaged.
|
|
35
|
+
*/
|
|
36
|
+
/** Breakpoint keys MUI's responsive `size`/`offset` objects accept. */
|
|
37
|
+
export declare const SPAN_BREAKPOINTS: readonly ["xs", "sm", "md", "lg", "xl"];
|
|
38
|
+
export type SpanBreakpoint = (typeof SPAN_BREAKPOINTS)[number];
|
|
39
|
+
/** One span: a column count, or one of MUI's two sizing keywords. */
|
|
40
|
+
export type SpanValue = number | 'auto' | 'grow';
|
|
41
|
+
export interface BreakpointSpan {
|
|
42
|
+
/**
|
|
43
|
+
* A value authored with NO breakpoint (`"6"`, `"auto"`). MUI applies it at
|
|
44
|
+
* every size, and it is mutually exclusive with {@link values} — the prop
|
|
45
|
+
* is either a scalar or an object, never both.
|
|
46
|
+
*/
|
|
47
|
+
base?: SpanValue;
|
|
48
|
+
/** Per-breakpoint values, normalized into {@link SPAN_BREAKPOINTS} order. */
|
|
49
|
+
values?: Partial<Record<SpanBreakpoint, SpanValue>>;
|
|
50
|
+
/** Set ONLY when the string is not a span this module can model. */
|
|
51
|
+
raw?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Reads a stored span string into {@link BreakpointSpan}.
|
|
55
|
+
*
|
|
56
|
+
* A partly-parseable list comes back as `raw`, never as a partial object: a
|
|
57
|
+
* half-applied breakpoint map is a layout that silently differs from what the
|
|
58
|
+
* author typed, which is worse than no layout at all.
|
|
59
|
+
*/
|
|
60
|
+
export declare function parseBreakpointSpan(value: string | number | undefined | null): BreakpointSpan;
|
|
61
|
+
/**
|
|
62
|
+
* Serializes a {@link BreakpointSpan} back to the single string that gets
|
|
63
|
+
* persisted. The inverse of {@link parseBreakpointSpan}: an empty span
|
|
64
|
+
* serializes to an empty string, never to a partial pair like `md:`.
|
|
65
|
+
*/
|
|
66
|
+
export declare function buildBreakpointSpan(span?: BreakpointSpan): string;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ /**
|
|
17
|
+
* The responsive span/offset syntax the besigner persists for MUI Grid
|
|
18
|
+
* (AGL-2486).
|
|
19
|
+
*
|
|
20
|
+
* A Grid cell's `size` and `offset` are stored as ONE string in the node's
|
|
21
|
+
* props — `"6"`, `"auto"`, `"xs:12 md:6"` — because MUI v6+ replaced the
|
|
22
|
+
* per-breakpoint `xs=`/`md=` props with a single value-or-object prop. That
|
|
23
|
+
* string is the persisted format and it does not change here; what changes is
|
|
24
|
+
* that the attributes panel no longer asks an author to TYPE it (a developer
|
|
25
|
+
* syntax nobody outside the docs knows). Parse and serialize live together in
|
|
26
|
+
* this shared module, the same arrangement `parseCssDimension`/
|
|
27
|
+
* `buildCssDimension` above have, so the editor and the renderer cannot drift
|
|
28
|
+
* into two different readings of the same stored string.
|
|
29
|
+
*
|
|
30
|
+
* `raw` is the escape hatch that keeps the editor from destroying what it
|
|
31
|
+
* cannot model — a half-typed pair, a `{{token}}` binding, an unknown
|
|
32
|
+
* breakpoint. The string comes back untouched instead of collapsing to
|
|
33
|
+
* whatever a lenient parse salvaged.
|
|
34
|
+
*/ /** Breakpoint keys MUI's responsive `size`/`offset` objects accept. */ export const SPAN_BREAKPOINTS = [
|
|
35
|
+
'xs',
|
|
36
|
+
'sm',
|
|
37
|
+
'md',
|
|
38
|
+
'lg',
|
|
39
|
+
'xl'
|
|
40
|
+
];
|
|
41
|
+
/** `12`, `-4`, `2.5` — a bare quantity with no breakpoint attached. */ const BARE_NUMBER = /^-?\d+(\.\d+)?$/;
|
|
42
|
+
/** `md:6`, `XS = auto` — one breakpoint pair, in either separator style. */ const PAIR = /^([a-z]+)\s*[:=]\s*(auto|grow|-?\d+(?:\.\d+)?)$/i;
|
|
43
|
+
const isBreakpoint = (key)=>SPAN_BREAKPOINTS.includes(key);
|
|
44
|
+
/**
|
|
45
|
+
* Reads a stored span string into {@link BreakpointSpan}.
|
|
46
|
+
*
|
|
47
|
+
* A partly-parseable list comes back as `raw`, never as a partial object: a
|
|
48
|
+
* half-applied breakpoint map is a layout that silently differs from what the
|
|
49
|
+
* author typed, which is worse than no layout at all.
|
|
50
|
+
*/ export function parseBreakpointSpan(value) {
|
|
51
|
+
// `value === 0` is a real span the author can mean, so this tests for
|
|
52
|
+
// absence explicitly rather than for falsiness (strictNullChecks is off
|
|
53
|
+
// repo-wide, and `!value` would swallow the zero).
|
|
54
|
+
if (value === undefined || value === null || value === '') return {};
|
|
55
|
+
if (typeof value === 'number') {
|
|
56
|
+
return Number.isFinite(value) ? {
|
|
57
|
+
base: value
|
|
58
|
+
} : {};
|
|
59
|
+
}
|
|
60
|
+
const text = `${value}`.trim();
|
|
61
|
+
if (!text) return {};
|
|
62
|
+
if (text === 'auto' || text === 'grow') return {
|
|
63
|
+
base: text
|
|
64
|
+
};
|
|
65
|
+
if (BARE_NUMBER.test(text)) return {
|
|
66
|
+
base: Number(text)
|
|
67
|
+
};
|
|
68
|
+
const pairs = text.split(/[\s,]+/).filter(Boolean);
|
|
69
|
+
const values = {};
|
|
70
|
+
for (const pair of pairs){
|
|
71
|
+
var _match_, _match_1;
|
|
72
|
+
const match = PAIR.exec(pair);
|
|
73
|
+
if (!match) return {
|
|
74
|
+
raw: text
|
|
75
|
+
};
|
|
76
|
+
const key = ((_match_ = match[1]) != null ? _match_ : '').toLowerCase();
|
|
77
|
+
if (!isBreakpoint(key)) return {
|
|
78
|
+
raw: text
|
|
79
|
+
};
|
|
80
|
+
const span = (_match_1 = match[2]) != null ? _match_1 : '';
|
|
81
|
+
values[key] = span === 'auto' || span === 'grow' ? span : Number(span);
|
|
82
|
+
}
|
|
83
|
+
if (!Object.keys(values).length) return {
|
|
84
|
+
raw: text
|
|
85
|
+
};
|
|
86
|
+
// Normalized to breakpoint order so the serialized string is stable no
|
|
87
|
+
// matter what order the author wrote the pairs in.
|
|
88
|
+
const ordered = {};
|
|
89
|
+
for (const key of SPAN_BREAKPOINTS){
|
|
90
|
+
if (values[key] !== undefined) ordered[key] = values[key];
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
values: ordered
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Serializes a {@link BreakpointSpan} back to the single string that gets
|
|
98
|
+
* persisted. The inverse of {@link parseBreakpointSpan}: an empty span
|
|
99
|
+
* serializes to an empty string, never to a partial pair like `md:`.
|
|
100
|
+
*/ export function buildBreakpointSpan(span) {
|
|
101
|
+
if (!span) return '';
|
|
102
|
+
if (span.raw !== undefined) return span.raw;
|
|
103
|
+
if (span.base !== undefined) return `${span.base}`;
|
|
104
|
+
const values = span.values;
|
|
105
|
+
if (!values) return '';
|
|
106
|
+
const pairs = [];
|
|
107
|
+
for (const key of SPAN_BREAKPOINTS){
|
|
108
|
+
const value = values[key];
|
|
109
|
+
if (value === undefined || value === null || value === '') {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
pairs.push(`${key}:${value}`);
|
|
113
|
+
}
|
|
114
|
+
return pairs.join(' ');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
//# sourceMappingURL=breakpoint-span.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/shared/data/enums/src/lib/breakpoint-span.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * The responsive span/offset syntax the besigner persists for MUI Grid\n * (AGL-2486).\n *\n * A Grid cell's `size` and `offset` are stored as ONE string in the node's\n * props — `\"6\"`, `\"auto\"`, `\"xs:12 md:6\"` — because MUI v6+ replaced the\n * per-breakpoint `xs=`/`md=` props with a single value-or-object prop. That\n * string is the persisted format and it does not change here; what changes is\n * that the attributes panel no longer asks an author to TYPE it (a developer\n * syntax nobody outside the docs knows). Parse and serialize live together in\n * this shared module, the same arrangement `parseCssDimension`/\n * `buildCssDimension` above have, so the editor and the renderer cannot drift\n * into two different readings of the same stored string.\n *\n * `raw` is the escape hatch that keeps the editor from destroying what it\n * cannot model — a half-typed pair, a `{{token}}` binding, an unknown\n * breakpoint. The string comes back untouched instead of collapsing to\n * whatever a lenient parse salvaged.\n */\n\n/** Breakpoint keys MUI's responsive `size`/`offset` objects accept. */\nexport const SPAN_BREAKPOINTS = ['xs', 'sm', 'md', 'lg', 'xl'] as const\n\nexport type SpanBreakpoint = (typeof SPAN_BREAKPOINTS)[number]\n\n/** One span: a column count, or one of MUI's two sizing keywords. */\nexport type SpanValue = number | 'auto' | 'grow'\n\nexport interface BreakpointSpan {\n /**\n * A value authored with NO breakpoint (`\"6\"`, `\"auto\"`). MUI applies it at\n * every size, and it is mutually exclusive with {@link values} — the prop\n * is either a scalar or an object, never both.\n */\n base?: SpanValue\n /** Per-breakpoint values, normalized into {@link SPAN_BREAKPOINTS} order. */\n values?: Partial<Record<SpanBreakpoint, SpanValue>>\n /** Set ONLY when the string is not a span this module can model. */\n raw?: string\n}\n\n/** `12`, `-4`, `2.5` — a bare quantity with no breakpoint attached. */\nconst BARE_NUMBER = /^-?\\d+(\\.\\d+)?$/\n/** `md:6`, `XS = auto` — one breakpoint pair, in either separator style. */\nconst PAIR = /^([a-z]+)\\s*[:=]\\s*(auto|grow|-?\\d+(?:\\.\\d+)?)$/i\n\nconst isBreakpoint = (key: string): key is SpanBreakpoint =>\n (SPAN_BREAKPOINTS as readonly string[]).includes(key)\n\n/**\n * Reads a stored span string into {@link BreakpointSpan}.\n *\n * A partly-parseable list comes back as `raw`, never as a partial object: a\n * half-applied breakpoint map is a layout that silently differs from what the\n * author typed, which is worse than no layout at all.\n */\nexport function parseBreakpointSpan(\n value: string | number | undefined | null,\n): BreakpointSpan {\n // `value === 0` is a real span the author can mean, so this tests for\n // absence explicitly rather than for falsiness (strictNullChecks is off\n // repo-wide, and `!value` would swallow the zero).\n if (value === undefined || value === null || value === '') return {}\n if (typeof value === 'number') {\n return Number.isFinite(value) ? { base: value } : {}\n }\n\n const text = `${value}`.trim()\n if (!text) return {}\n if (text === 'auto' || text === 'grow') return { base: text }\n if (BARE_NUMBER.test(text)) return { base: Number(text) }\n\n const pairs = text.split(/[\\s,]+/).filter(Boolean)\n const values: Partial<Record<SpanBreakpoint, SpanValue>> = {}\n for (const pair of pairs) {\n const match = PAIR.exec(pair)\n if (!match) return { raw: text }\n const key = (match[1] ?? '').toLowerCase()\n if (!isBreakpoint(key)) return { raw: text }\n const span = match[2] ?? ''\n values[key] =\n span === 'auto' || span === 'grow' ? (span as SpanValue) : Number(span)\n }\n if (!Object.keys(values).length) return { raw: text }\n\n // Normalized to breakpoint order so the serialized string is stable no\n // matter what order the author wrote the pairs in.\n const ordered: Partial<Record<SpanBreakpoint, SpanValue>> = {}\n for (const key of SPAN_BREAKPOINTS) {\n if (values[key] !== undefined) ordered[key] = values[key]\n }\n return { values: ordered }\n}\n\n/**\n * Serializes a {@link BreakpointSpan} back to the single string that gets\n * persisted. The inverse of {@link parseBreakpointSpan}: an empty span\n * serializes to an empty string, never to a partial pair like `md:`.\n */\nexport function buildBreakpointSpan(span?: BreakpointSpan): string {\n if (!span) return ''\n if (span.raw !== undefined) return span.raw\n if (span.base !== undefined) return `${span.base}`\n const values = span.values\n if (!values) return ''\n const pairs: string[] = []\n for (const key of SPAN_BREAKPOINTS) {\n const value = values[key]\n if (value === undefined || value === null || (value as unknown) === '') {\n continue\n }\n pairs.push(`${key}:${value}`)\n }\n return pairs.join(' ')\n}\n"],"names":["SPAN_BREAKPOINTS","BARE_NUMBER","PAIR","isBreakpoint","key","includes","parseBreakpointSpan","value","undefined","Number","isFinite","base","text","trim","test","pairs","split","filter","Boolean","values","pair","match","exec","raw","toLowerCase","span","Object","keys","length","ordered","buildBreakpointSpan","push","join"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;CAkBC,GAED,qEAAqE,GACrE,OAAO,MAAMA,mBAAmB;IAAC;IAAM;IAAM;IAAM;IAAM;CAAK,CAAS;AAoBvE,qEAAqE,GACrE,MAAMC,cAAc;AACpB,0EAA0E,GAC1E,MAAMC,OAAO;AAEb,MAAMC,eAAe,CAACC,MACpB,AAACJ,iBAAuCK,QAAQ,CAACD;AAEnD;;;;;;CAMC,GACD,OAAO,SAASE,oBACdC,KAAyC;IAEzC,sEAAsE;IACtE,wEAAwE;IACxE,mDAAmD;IACnD,IAAIA,UAAUC,aAAaD,UAAU,QAAQA,UAAU,IAAI,OAAO,CAAC;IACnE,IAAI,OAAOA,UAAU,UAAU;QAC7B,OAAOE,OAAOC,QAAQ,CAACH,SAAS;YAAEI,MAAMJ;QAAM,IAAI,CAAC;IACrD;IAEA,MAAMK,OAAO,GAAGL,OAAO,CAACM,IAAI;IAC5B,IAAI,CAACD,MAAM,OAAO,CAAC;IACnB,IAAIA,SAAS,UAAUA,SAAS,QAAQ,OAAO;QAAED,MAAMC;IAAK;IAC5D,IAAIX,YAAYa,IAAI,CAACF,OAAO,OAAO;QAAED,MAAMF,OAAOG;IAAM;IAExD,MAAMG,QAAQH,KAAKI,KAAK,CAAC,UAAUC,MAAM,CAACC;IAC1C,MAAMC,SAAqD,CAAC;IAC5D,KAAK,MAAMC,QAAQL,MAAO;YAGXM,SAEAA;QAJb,MAAMA,QAAQnB,KAAKoB,IAAI,CAACF;QACxB,IAAI,CAACC,OAAO,OAAO;YAAEE,KAAKX;QAAK;QAC/B,MAAMR,MAAM,EAACiB,UAAAA,KAAK,CAAC,EAAE,YAARA,UAAY,IAAIG,WAAW;QACxC,IAAI,CAACrB,aAAaC,MAAM,OAAO;YAAEmB,KAAKX;QAAK;QAC3C,MAAMa,QAAOJ,WAAAA,KAAK,CAAC,EAAE,YAARA,WAAY;QACzBF,MAAM,CAACf,IAAI,GACTqB,SAAS,UAAUA,SAAS,SAAUA,OAAqBhB,OAAOgB;IACtE;IACA,IAAI,CAACC,OAAOC,IAAI,CAACR,QAAQS,MAAM,EAAE,OAAO;QAAEL,KAAKX;IAAK;IAEpD,uEAAuE;IACvE,mDAAmD;IACnD,MAAMiB,UAAsD,CAAC;IAC7D,KAAK,MAAMzB,OAAOJ,iBAAkB;QAClC,IAAImB,MAAM,CAACf,IAAI,KAAKI,WAAWqB,OAAO,CAACzB,IAAI,GAAGe,MAAM,CAACf,IAAI;IAC3D;IACA,OAAO;QAAEe,QAAQU;IAAQ;AAC3B;AAEA;;;;CAIC,GACD,OAAO,SAASC,oBAAoBL,IAAqB;IACvD,IAAI,CAACA,MAAM,OAAO;IAClB,IAAIA,KAAKF,GAAG,KAAKf,WAAW,OAAOiB,KAAKF,GAAG;IAC3C,IAAIE,KAAKd,IAAI,KAAKH,WAAW,OAAO,GAAGiB,KAAKd,IAAI,EAAE;IAClD,MAAMQ,SAASM,KAAKN,MAAM;IAC1B,IAAI,CAACA,QAAQ,OAAO;IACpB,MAAMJ,QAAkB,EAAE;IAC1B,KAAK,MAAMX,OAAOJ,iBAAkB;QAClC,MAAMO,QAAQY,MAAM,CAACf,IAAI;QACzB,IAAIG,UAAUC,aAAaD,UAAU,QAAQ,AAACA,UAAsB,IAAI;YACtE;QACF;QACAQ,MAAMgB,IAAI,CAAC,GAAG3B,IAAI,CAAC,EAAEG,OAAO;IAC9B;IACA,OAAOQ,MAAMiB,IAAI,CAAC;AACpB"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* The grid behind the Table element (AGL-2543).
|
|
19
|
+
*
|
|
20
|
+
* ONE parser, shared by the renderer and the attributes-panel editor. Two
|
|
21
|
+
* implementations of "what does this string mean" is how a table comes to
|
|
22
|
+
* look right in the canvas and wrong on the page, and the two are written by
|
|
23
|
+
* different people months apart.
|
|
24
|
+
*
|
|
25
|
+
* The persisted prop is a single string, following the house pattern used by
|
|
26
|
+
* `CSS_DIMENSION` and `CSS_GRADIENT`: the rich editor is an input affordance,
|
|
27
|
+
* not a shape change, so nothing downstream has to learn a new type and a
|
|
28
|
+
* value the editor cannot model is never clobbered.
|
|
29
|
+
*
|
|
30
|
+
* Pipe-delimited, one row per line — deliberately the markdown table body's
|
|
31
|
+
* own syntax, so the comparison tables already authored in Markdown elements
|
|
32
|
+
* paste straight in. A leading and trailing pipe are optional, which is what
|
|
33
|
+
* a copied markdown table has.
|
|
34
|
+
*/
|
|
35
|
+
/** Column alignments the editor offers and the renderer honours. */
|
|
36
|
+
export declare const DATA_TABLE_ALIGNMENTS: readonly ["left", "center", "right"];
|
|
37
|
+
export type DataTableAlignment = (typeof DATA_TABLE_ALIGNMENTS)[number];
|
|
38
|
+
export declare const DATA_TABLE_ALIGNMENT_DEFAULT: DataTableAlignment;
|
|
39
|
+
/**
|
|
40
|
+
* The alignments a pasted markdown divider row encodes, or `null` when the
|
|
41
|
+
* line is not one. `:--` is left, `:-:` centre, `--:` right.
|
|
42
|
+
*/
|
|
43
|
+
export declare function alignmentsFromDivider(line: string): DataTableAlignment[] | null;
|
|
44
|
+
/**
|
|
45
|
+
* The grid a stored value describes.
|
|
46
|
+
*
|
|
47
|
+
* Rows are padded to the widest one so the renderer and the editor always see
|
|
48
|
+
* a rectangle: a short row is an authoring accident, and a table that renders
|
|
49
|
+
* with a missing cell is harder to fix than one with an empty one.
|
|
50
|
+
*/
|
|
51
|
+
export declare function parseDataTableRows(value: unknown): string[][];
|
|
52
|
+
/**
|
|
53
|
+
* The alignments a stored value carries, padded to the table's width.
|
|
54
|
+
*
|
|
55
|
+
* Alignment lives in the SAME string as the grid, as a markdown divider row,
|
|
56
|
+
* rather than in a prop of its own. A field editor is handed one prop, so a
|
|
57
|
+
* second prop could not be reached from the grid's own per-column controls —
|
|
58
|
+
* and encoding it the way markdown already does means a pasted table keeps
|
|
59
|
+
* the alignment it was authored with instead of silently flattening to left.
|
|
60
|
+
*/
|
|
61
|
+
export declare function readDataTableAlignments(value: unknown, columnCount: number): DataTableAlignment[];
|
|
62
|
+
/** A row of a table the author pasted, and the alignments it carried. */
|
|
63
|
+
export interface PastedDataTable {
|
|
64
|
+
rows: string[][];
|
|
65
|
+
alignments: DataTableAlignment[];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The table a pasted string describes, or `null` when it is not one
|
|
69
|
+
* (AGL-2568).
|
|
70
|
+
*
|
|
71
|
+
* Reading it is the migration path off the Markdown workaround: the tables
|
|
72
|
+
* this element replaces are already authored as pipe syntax, and the
|
|
73
|
+
* alternative to importing them is retyping thirty cells of dated competitor
|
|
74
|
+
* pricing by hand, which is the content least safe to retype.
|
|
75
|
+
*
|
|
76
|
+
* `null` rather than a best effort is the important half. This runs on every
|
|
77
|
+
* paste into every cell, and an author pasting `Pro | Business` as a cell
|
|
78
|
+
* VALUE must get those characters, not a two-column table. So a paste is only
|
|
79
|
+
* read as a table when it could not sensibly be anything else: more than one
|
|
80
|
+
* line, every one of them carrying an unescaped pipe, and at least two
|
|
81
|
+
* columns once parsed. A single line never qualifies, however many pipes it
|
|
82
|
+
* has.
|
|
83
|
+
*/
|
|
84
|
+
export declare function readPastedDataTable(value: unknown): PastedDataTable | null;
|
|
85
|
+
/**
|
|
86
|
+
* The stored form of a grid plus its alignments.
|
|
87
|
+
*
|
|
88
|
+
* The divider is written after the first row, where markdown puts it, and
|
|
89
|
+
* only when some column is not the default — an all-left table stays a plain
|
|
90
|
+
* pipe grid rather than growing a row of dashes nobody asked for.
|
|
91
|
+
*/
|
|
92
|
+
export declare function serializeDataTable(rows: string[][], alignments?: readonly DataTableAlignment[]): string;
|
|
93
|
+
/**
|
|
94
|
+
* The 1-based column to emphasise, or `0` for none.
|
|
95
|
+
*
|
|
96
|
+
* 1-based because the control says "column 3" to an author who is looking at
|
|
97
|
+
* the third column; `0` rather than `null` so "none" survives a form field
|
|
98
|
+
* that only speaks numbers. Out-of-range values mean none, so deleting a
|
|
99
|
+
* column cannot leave a table pointing at one that is gone.
|
|
100
|
+
*/
|
|
101
|
+
export declare function normalizeEmphasisColumn(value: unknown, columnCount: number): number;
|
|
102
|
+
/** A grid with `count` empty columns appended to every row. */
|
|
103
|
+
export declare function withColumnAdded(rows: string[][], at?: number): string[][];
|
|
104
|
+
export declare function withColumnRemoved(rows: string[][], at: number): string[][];
|
|
105
|
+
export declare function withRowAdded(rows: string[][], at?: number): string[][];
|
|
106
|
+
export declare function withRowRemoved(rows: string[][], at: number): string[][];
|
|
107
|
+
export declare function withCellSet(rows: string[][], rowIndex: number, columnIndex: number, value: string): string[][];
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ /**
|
|
17
|
+
* The grid behind the Table element (AGL-2543).
|
|
18
|
+
*
|
|
19
|
+
* ONE parser, shared by the renderer and the attributes-panel editor. Two
|
|
20
|
+
* implementations of "what does this string mean" is how a table comes to
|
|
21
|
+
* look right in the canvas and wrong on the page, and the two are written by
|
|
22
|
+
* different people months apart.
|
|
23
|
+
*
|
|
24
|
+
* The persisted prop is a single string, following the house pattern used by
|
|
25
|
+
* `CSS_DIMENSION` and `CSS_GRADIENT`: the rich editor is an input affordance,
|
|
26
|
+
* not a shape change, so nothing downstream has to learn a new type and a
|
|
27
|
+
* value the editor cannot model is never clobbered.
|
|
28
|
+
*
|
|
29
|
+
* Pipe-delimited, one row per line — deliberately the markdown table body's
|
|
30
|
+
* own syntax, so the comparison tables already authored in Markdown elements
|
|
31
|
+
* paste straight in. A leading and trailing pipe are optional, which is what
|
|
32
|
+
* a copied markdown table has.
|
|
33
|
+
*/ /** Column alignments the editor offers and the renderer honours. */ export const DATA_TABLE_ALIGNMENTS = [
|
|
34
|
+
'left',
|
|
35
|
+
'center',
|
|
36
|
+
'right'
|
|
37
|
+
];
|
|
38
|
+
export const DATA_TABLE_ALIGNMENT_DEFAULT = 'left';
|
|
39
|
+
/**
|
|
40
|
+
* A markdown alignment divider — `|---|:--:|---:|`.
|
|
41
|
+
*
|
|
42
|
+
* Dropped rather than rendered as a row of dashes. It carries no content, and
|
|
43
|
+
* an author pasting a markdown table has no reason to expect it to survive as
|
|
44
|
+
* data; its alignment intent is read out separately by
|
|
45
|
+
* {@link alignmentsFromDivider}.
|
|
46
|
+
*/ const DIVIDER_CELL = /^:?-{2,}:?$/;
|
|
47
|
+
function splitRow(line) {
|
|
48
|
+
// `\|` is a literal pipe, so a cell may contain one. Split on unescaped
|
|
49
|
+
// pipes only, then unescape — doing it in the other order would turn an
|
|
50
|
+
// escaped pipe into a column break.
|
|
51
|
+
const cells = line.replace(/^\s*\|/, '').replace(/\|\s*$/, '').split(/(?<!\\)\|/).map((cell)=>cell.replace(/\\\|/g, '|').trim());
|
|
52
|
+
return cells;
|
|
53
|
+
}
|
|
54
|
+
function isDividerRow(cells) {
|
|
55
|
+
return cells.length > 0 && cells.every((cell)=>DIVIDER_CELL.test(cell));
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The alignments a pasted markdown divider row encodes, or `null` when the
|
|
59
|
+
* line is not one. `:--` is left, `:-:` centre, `--:` right.
|
|
60
|
+
*/ export function alignmentsFromDivider(line) {
|
|
61
|
+
const cells = splitRow(String(line != null ? line : ''));
|
|
62
|
+
if (!isDividerRow(cells)) return null;
|
|
63
|
+
return cells.map((cell)=>{
|
|
64
|
+
const left = cell.startsWith(':');
|
|
65
|
+
const right = cell.endsWith(':');
|
|
66
|
+
if (left && right) return 'center';
|
|
67
|
+
if (right) return 'right';
|
|
68
|
+
return 'left';
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The grid a stored value describes.
|
|
73
|
+
*
|
|
74
|
+
* Rows are padded to the widest one so the renderer and the editor always see
|
|
75
|
+
* a rectangle: a short row is an authoring accident, and a table that renders
|
|
76
|
+
* with a missing cell is harder to fix than one with an empty one.
|
|
77
|
+
*/ export function parseDataTableRows(value) {
|
|
78
|
+
const text = typeof value === 'string' ? value : '';
|
|
79
|
+
const rows = text.split(/\r?\n/).map((line)=>line.trim()).filter((line)=>line.length > 0).map(splitRow).filter((cells)=>!isDividerRow(cells));
|
|
80
|
+
const width = rows.reduce((widest, row)=>Math.max(widest, row.length), 0);
|
|
81
|
+
return rows.map((row)=>[
|
|
82
|
+
...row,
|
|
83
|
+
...Array.from({
|
|
84
|
+
length: width - row.length
|
|
85
|
+
}, ()=>'')
|
|
86
|
+
]);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The alignments a stored value carries, padded to the table's width.
|
|
90
|
+
*
|
|
91
|
+
* Alignment lives in the SAME string as the grid, as a markdown divider row,
|
|
92
|
+
* rather than in a prop of its own. A field editor is handed one prop, so a
|
|
93
|
+
* second prop could not be reached from the grid's own per-column controls —
|
|
94
|
+
* and encoding it the way markdown already does means a pasted table keeps
|
|
95
|
+
* the alignment it was authored with instead of silently flattening to left.
|
|
96
|
+
*/ export function readDataTableAlignments(value, columnCount) {
|
|
97
|
+
const found = String(value != null ? value : '').split(/\r?\n/).map((line)=>alignmentsFromDivider(line)).find((alignments)=>alignments !== null);
|
|
98
|
+
return Array.from({
|
|
99
|
+
length: Math.max(0, columnCount)
|
|
100
|
+
}, (_, index)=>{
|
|
101
|
+
const candidate = found == null ? void 0 : found[index];
|
|
102
|
+
return candidate != null ? candidate : DATA_TABLE_ALIGNMENT_DEFAULT;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/** A pipe that breaks a column, as opposed to a `\|` inside a cell. */ function hasUnescapedPipe(line) {
|
|
106
|
+
return /(?<!\\)\|/.test(line);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* The table a pasted string describes, or `null` when it is not one
|
|
110
|
+
* (AGL-2568).
|
|
111
|
+
*
|
|
112
|
+
* Reading it is the migration path off the Markdown workaround: the tables
|
|
113
|
+
* this element replaces are already authored as pipe syntax, and the
|
|
114
|
+
* alternative to importing them is retyping thirty cells of dated competitor
|
|
115
|
+
* pricing by hand, which is the content least safe to retype.
|
|
116
|
+
*
|
|
117
|
+
* `null` rather than a best effort is the important half. This runs on every
|
|
118
|
+
* paste into every cell, and an author pasting `Pro | Business` as a cell
|
|
119
|
+
* VALUE must get those characters, not a two-column table. So a paste is only
|
|
120
|
+
* read as a table when it could not sensibly be anything else: more than one
|
|
121
|
+
* line, every one of them carrying an unescaped pipe, and at least two
|
|
122
|
+
* columns once parsed. A single line never qualifies, however many pipes it
|
|
123
|
+
* has.
|
|
124
|
+
*/ export function readPastedDataTable(value) {
|
|
125
|
+
var _ref;
|
|
126
|
+
var _rows_;
|
|
127
|
+
const text = typeof value === 'string' ? value : '';
|
|
128
|
+
const lines = text.split(/\r?\n/).map((line)=>line.trim()).filter((line)=>line.length > 0);
|
|
129
|
+
if (lines.length < 2 || !lines.every(hasUnescapedPipe)) return null;
|
|
130
|
+
const rows = parseDataTableRows(text);
|
|
131
|
+
const width = (_ref = (_rows_ = rows[0]) == null ? void 0 : _rows_.length) != null ? _ref : 0;
|
|
132
|
+
if (rows.length === 0 || width < 2) return null;
|
|
133
|
+
return {
|
|
134
|
+
rows,
|
|
135
|
+
alignments: readDataTableAlignments(text, width)
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/** The markdown divider encoding one column's alignment. */ function dividerCell(alignment) {
|
|
139
|
+
if (alignment === 'center') return ':---:';
|
|
140
|
+
if (alignment === 'right') return '---:';
|
|
141
|
+
return '---';
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* The stored form of a grid plus its alignments.
|
|
145
|
+
*
|
|
146
|
+
* The divider is written after the first row, where markdown puts it, and
|
|
147
|
+
* only when some column is not the default — an all-left table stays a plain
|
|
148
|
+
* pipe grid rather than growing a row of dashes nobody asked for.
|
|
149
|
+
*/ export function serializeDataTable(rows, alignments) {
|
|
150
|
+
var _ref, _ref1;
|
|
151
|
+
var _rows_;
|
|
152
|
+
const body = (rows != null ? rows : []).map((row)=>(row != null ? row : []).map((cell)=>String(cell != null ? cell : '').replace(/\|/g, '\\|')).join(' | '));
|
|
153
|
+
const meaningful = (_ref = alignments == null ? void 0 : alignments.some((alignment)=>alignment && alignment !== DATA_TABLE_ALIGNMENT_DEFAULT)) != null ? _ref : false;
|
|
154
|
+
if (!meaningful || body.length === 0) return body.join('\n');
|
|
155
|
+
const width = (_ref1 = (_rows_ = rows[0]) == null ? void 0 : _rows_.length) != null ? _ref1 : 0;
|
|
156
|
+
const divider = Array.from({
|
|
157
|
+
length: width
|
|
158
|
+
}, (_, index)=>{
|
|
159
|
+
var _ref;
|
|
160
|
+
return dividerCell((_ref = alignments == null ? void 0 : alignments[index]) != null ? _ref : DATA_TABLE_ALIGNMENT_DEFAULT);
|
|
161
|
+
}).join(' | ');
|
|
162
|
+
return [
|
|
163
|
+
body[0],
|
|
164
|
+
divider,
|
|
165
|
+
...body.slice(1)
|
|
166
|
+
].join('\n');
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* The 1-based column to emphasise, or `0` for none.
|
|
170
|
+
*
|
|
171
|
+
* 1-based because the control says "column 3" to an author who is looking at
|
|
172
|
+
* the third column; `0` rather than `null` so "none" survives a form field
|
|
173
|
+
* that only speaks numbers. Out-of-range values mean none, so deleting a
|
|
174
|
+
* column cannot leave a table pointing at one that is gone.
|
|
175
|
+
*/ export function normalizeEmphasisColumn(value, columnCount) {
|
|
176
|
+
const parsed = Math.trunc(Number(value));
|
|
177
|
+
if (!Number.isFinite(parsed) || parsed < 1 || parsed > columnCount) return 0;
|
|
178
|
+
return parsed;
|
|
179
|
+
}
|
|
180
|
+
/** A grid with `count` empty columns appended to every row. */ export function withColumnAdded(rows, at) {
|
|
181
|
+
var _ref;
|
|
182
|
+
var _rows_;
|
|
183
|
+
const width = (_ref = (_rows_ = rows[0]) == null ? void 0 : _rows_.length) != null ? _ref : 0;
|
|
184
|
+
const index = at == null ? width : Math.max(0, Math.min(at, width));
|
|
185
|
+
return (rows.length ? rows : [
|
|
186
|
+
[]
|
|
187
|
+
]).map((row)=>{
|
|
188
|
+
const next = [
|
|
189
|
+
...row
|
|
190
|
+
];
|
|
191
|
+
next.splice(index, 0, '');
|
|
192
|
+
return next;
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
export function withColumnRemoved(rows, at) {
|
|
196
|
+
var _ref;
|
|
197
|
+
var _rows_;
|
|
198
|
+
const width = (_ref = (_rows_ = rows[0]) == null ? void 0 : _rows_.length) != null ? _ref : 0;
|
|
199
|
+
// Never leave a table with no columns: a zero-column grid cannot be typed
|
|
200
|
+
// back into through the editor, so the last column is not removable.
|
|
201
|
+
if (width <= 1) return rows;
|
|
202
|
+
return rows.map((row)=>row.filter((_, index)=>index !== at));
|
|
203
|
+
}
|
|
204
|
+
export function withRowAdded(rows, at) {
|
|
205
|
+
var _ref;
|
|
206
|
+
var _rows_;
|
|
207
|
+
const width = (_ref = (_rows_ = rows[0]) == null ? void 0 : _rows_.length) != null ? _ref : 1;
|
|
208
|
+
const blank = Array.from({
|
|
209
|
+
length: width
|
|
210
|
+
}, ()=>'');
|
|
211
|
+
const next = [
|
|
212
|
+
...rows
|
|
213
|
+
];
|
|
214
|
+
next.splice(at == null ? rows.length : Math.max(0, Math.min(at, rows.length)), 0, blank);
|
|
215
|
+
return next;
|
|
216
|
+
}
|
|
217
|
+
export function withRowRemoved(rows, at) {
|
|
218
|
+
if (rows.length <= 1) return rows;
|
|
219
|
+
return rows.filter((_, index)=>index !== at);
|
|
220
|
+
}
|
|
221
|
+
export function withCellSet(rows, rowIndex, columnIndex, value) {
|
|
222
|
+
return rows.map((row, r)=>r === rowIndex ? row.map((cell, c)=>c === columnIndex ? value : cell) : row);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
//# sourceMappingURL=data-table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/shared/data/enums/src/lib/data-table.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * The grid behind the Table element (AGL-2543).\n *\n * ONE parser, shared by the renderer and the attributes-panel editor. Two\n * implementations of \"what does this string mean\" is how a table comes to\n * look right in the canvas and wrong on the page, and the two are written by\n * different people months apart.\n *\n * The persisted prop is a single string, following the house pattern used by\n * `CSS_DIMENSION` and `CSS_GRADIENT`: the rich editor is an input affordance,\n * not a shape change, so nothing downstream has to learn a new type and a\n * value the editor cannot model is never clobbered.\n *\n * Pipe-delimited, one row per line — deliberately the markdown table body's\n * own syntax, so the comparison tables already authored in Markdown elements\n * paste straight in. A leading and trailing pipe are optional, which is what\n * a copied markdown table has.\n */\n\n/** Column alignments the editor offers and the renderer honours. */\nexport const DATA_TABLE_ALIGNMENTS = ['left', 'center', 'right'] as const\n\nexport type DataTableAlignment = (typeof DATA_TABLE_ALIGNMENTS)[number]\n\nexport const DATA_TABLE_ALIGNMENT_DEFAULT: DataTableAlignment = 'left'\n\n/**\n * A markdown alignment divider — `|---|:--:|---:|`.\n *\n * Dropped rather than rendered as a row of dashes. It carries no content, and\n * an author pasting a markdown table has no reason to expect it to survive as\n * data; its alignment intent is read out separately by\n * {@link alignmentsFromDivider}.\n */\nconst DIVIDER_CELL = /^:?-{2,}:?$/\n\nfunction splitRow(line: string): string[] {\n // `\\|` is a literal pipe, so a cell may contain one. Split on unescaped\n // pipes only, then unescape — doing it in the other order would turn an\n // escaped pipe into a column break.\n const cells = line\n .replace(/^\\s*\\|/, '')\n .replace(/\\|\\s*$/, '')\n .split(/(?<!\\\\)\\|/)\n .map((cell) => cell.replace(/\\\\\\|/g, '|').trim())\n return cells\n}\n\nfunction isDividerRow(cells: string[]): boolean {\n return cells.length > 0 && cells.every((cell) => DIVIDER_CELL.test(cell))\n}\n\n/**\n * The alignments a pasted markdown divider row encodes, or `null` when the\n * line is not one. `:--` is left, `:-:` centre, `--:` right.\n */\nexport function alignmentsFromDivider(\n line: string,\n): DataTableAlignment[] | null {\n const cells = splitRow(String(line ?? ''))\n if (!isDividerRow(cells)) return null\n return cells.map((cell) => {\n const left = cell.startsWith(':')\n const right = cell.endsWith(':')\n if (left && right) return 'center'\n if (right) return 'right'\n return 'left'\n })\n}\n\n/**\n * The grid a stored value describes.\n *\n * Rows are padded to the widest one so the renderer and the editor always see\n * a rectangle: a short row is an authoring accident, and a table that renders\n * with a missing cell is harder to fix than one with an empty one.\n */\nexport function parseDataTableRows(value: unknown): string[][] {\n const text = typeof value === 'string' ? value : ''\n const rows = text\n .split(/\\r?\\n/)\n .map((line) => line.trim())\n .filter((line) => line.length > 0)\n .map(splitRow)\n .filter((cells) => !isDividerRow(cells))\n const width = rows.reduce((widest, row) => Math.max(widest, row.length), 0)\n return rows.map((row) => [\n ...row,\n ...Array.from({ length: width - row.length }, () => ''),\n ])\n}\n\n/**\n * The alignments a stored value carries, padded to the table's width.\n *\n * Alignment lives in the SAME string as the grid, as a markdown divider row,\n * rather than in a prop of its own. A field editor is handed one prop, so a\n * second prop could not be reached from the grid's own per-column controls —\n * and encoding it the way markdown already does means a pasted table keeps\n * the alignment it was authored with instead of silently flattening to left.\n */\nexport function readDataTableAlignments(\n value: unknown,\n columnCount: number,\n): DataTableAlignment[] {\n const found = String(value ?? '')\n .split(/\\r?\\n/)\n .map((line) => alignmentsFromDivider(line))\n .find((alignments) => alignments !== null)\n return Array.from({ length: Math.max(0, columnCount) }, (_, index) => {\n const candidate = found?.[index]\n return candidate ?? DATA_TABLE_ALIGNMENT_DEFAULT\n })\n}\n\n/** A row of a table the author pasted, and the alignments it carried. */\nexport interface PastedDataTable {\n rows: string[][]\n alignments: DataTableAlignment[]\n}\n\n/** A pipe that breaks a column, as opposed to a `\\|` inside a cell. */\nfunction hasUnescapedPipe(line: string): boolean {\n return /(?<!\\\\)\\|/.test(line)\n}\n\n/**\n * The table a pasted string describes, or `null` when it is not one\n * (AGL-2568).\n *\n * Reading it is the migration path off the Markdown workaround: the tables\n * this element replaces are already authored as pipe syntax, and the\n * alternative to importing them is retyping thirty cells of dated competitor\n * pricing by hand, which is the content least safe to retype.\n *\n * `null` rather than a best effort is the important half. This runs on every\n * paste into every cell, and an author pasting `Pro | Business` as a cell\n * VALUE must get those characters, not a two-column table. So a paste is only\n * read as a table when it could not sensibly be anything else: more than one\n * line, every one of them carrying an unescaped pipe, and at least two\n * columns once parsed. A single line never qualifies, however many pipes it\n * has.\n */\nexport function readPastedDataTable(value: unknown): PastedDataTable | null {\n const text = typeof value === 'string' ? value : ''\n const lines = text\n .split(/\\r?\\n/)\n .map((line) => line.trim())\n .filter((line) => line.length > 0)\n if (lines.length < 2 || !lines.every(hasUnescapedPipe)) return null\n const rows = parseDataTableRows(text)\n const width = rows[0]?.length ?? 0\n if (rows.length === 0 || width < 2) return null\n return { rows, alignments: readDataTableAlignments(text, width) }\n}\n\n/** The markdown divider encoding one column's alignment. */\nfunction dividerCell(alignment: DataTableAlignment): string {\n if (alignment === 'center') return ':---:'\n if (alignment === 'right') return '---:'\n return '---'\n}\n\n/**\n * The stored form of a grid plus its alignments.\n *\n * The divider is written after the first row, where markdown puts it, and\n * only when some column is not the default — an all-left table stays a plain\n * pipe grid rather than growing a row of dashes nobody asked for.\n */\nexport function serializeDataTable(\n rows: string[][],\n alignments?: readonly DataTableAlignment[],\n): string {\n const body = (rows ?? []).map((row) =>\n (row ?? [])\n .map((cell) => String(cell ?? '').replace(/\\|/g, '\\\\|'))\n .join(' | '),\n )\n const meaningful =\n alignments?.some(\n (alignment) => alignment && alignment !== DATA_TABLE_ALIGNMENT_DEFAULT,\n ) ?? false\n if (!meaningful || body.length === 0) return body.join('\\n')\n const width = rows[0]?.length ?? 0\n const divider = Array.from({ length: width }, (_, index) =>\n dividerCell(alignments?.[index] ?? DATA_TABLE_ALIGNMENT_DEFAULT),\n ).join(' | ')\n return [body[0], divider, ...body.slice(1)].join('\\n')\n}\n\n/**\n * The 1-based column to emphasise, or `0` for none.\n *\n * 1-based because the control says \"column 3\" to an author who is looking at\n * the third column; `0` rather than `null` so \"none\" survives a form field\n * that only speaks numbers. Out-of-range values mean none, so deleting a\n * column cannot leave a table pointing at one that is gone.\n */\nexport function normalizeEmphasisColumn(\n value: unknown,\n columnCount: number,\n): number {\n const parsed = Math.trunc(Number(value))\n if (!Number.isFinite(parsed) || parsed < 1 || parsed > columnCount) return 0\n return parsed\n}\n\n/** A grid with `count` empty columns appended to every row. */\nexport function withColumnAdded(rows: string[][], at?: number): string[][] {\n const width = rows[0]?.length ?? 0\n const index = at == null ? width : Math.max(0, Math.min(at, width))\n return (rows.length ? rows : [[]]).map((row) => {\n const next = [...row]\n next.splice(index, 0, '')\n return next\n })\n}\n\nexport function withColumnRemoved(rows: string[][], at: number): string[][] {\n const width = rows[0]?.length ?? 0\n // Never leave a table with no columns: a zero-column grid cannot be typed\n // back into through the editor, so the last column is not removable.\n if (width <= 1) return rows\n return rows.map((row) => row.filter((_, index) => index !== at))\n}\n\nexport function withRowAdded(rows: string[][], at?: number): string[][] {\n const width = rows[0]?.length ?? 1\n const blank = Array.from({ length: width }, () => '')\n const next = [...rows]\n next.splice(at == null ? rows.length : Math.max(0, Math.min(at, rows.length)), 0, blank)\n return next\n}\n\nexport function withRowRemoved(rows: string[][], at: number): string[][] {\n if (rows.length <= 1) return rows\n return rows.filter((_, index) => index !== at)\n}\n\nexport function withCellSet(\n rows: string[][],\n rowIndex: number,\n columnIndex: number,\n value: string,\n): string[][] {\n return rows.map((row, r) =>\n r === rowIndex\n ? row.map((cell, c) => (c === columnIndex ? value : cell))\n : row,\n )\n}\n"],"names":["DATA_TABLE_ALIGNMENTS","DATA_TABLE_ALIGNMENT_DEFAULT","DIVIDER_CELL","splitRow","line","cells","replace","split","map","cell","trim","isDividerRow","length","every","test","alignmentsFromDivider","String","left","startsWith","right","endsWith","parseDataTableRows","value","text","rows","filter","width","reduce","widest","row","Math","max","Array","from","readDataTableAlignments","columnCount","found","find","alignments","_","index","candidate","hasUnescapedPipe","readPastedDataTable","lines","dividerCell","alignment","serializeDataTable","body","join","meaningful","some","divider","slice","normalizeEmphasisColumn","parsed","trunc","Number","isFinite","withColumnAdded","at","min","next","splice","withColumnRemoved","withRowAdded","blank","withRowRemoved","withCellSet","rowIndex","columnIndex","r","c"],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2023 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import { type AuthError, AuthErrorCodes, type OAuthCredential, type UserCredential } from 'firebase/auth';
|
|
18
|
+
export declare const AuthAppErrorCodes: {
|
|
19
|
+
/** Google resolved SSO against the wrong signed-in account. */
|
|
20
|
+
readonly SSO_ACCOUNT_MISMATCH: "auth/sso-account-mismatch";
|
|
21
|
+
/** No SSO configured for the email domain the user typed. */
|
|
22
|
+
readonly SSO_NOT_CONFIGURED: "auth/sso-not-configured";
|
|
23
|
+
/** The user submitted the SSO gate without a usable email. */
|
|
24
|
+
readonly SSO_INPUT_REQUIRED: "auth/sso-input-required";
|
|
25
|
+
/** Authenticated with the IdP, but not a member of the organization. */
|
|
26
|
+
readonly SSO_NOT_AUTHORIZED: "auth/sso-not-authorized";
|
|
27
|
+
/**
|
|
28
|
+
* The IdP window closed without a result. On the SSO path this is NOT the
|
|
29
|
+
* plain cancel it is elsewhere: when Google refuses the SAML request it
|
|
30
|
+
* renders its own error page inside that window, so the user closing it
|
|
31
|
+
* looks identical to a cancel. Suppressing it — as the shared
|
|
32
|
+
* `AuthErrorIgnore` list does for popup-closed — is what left the desktop
|
|
33
|
+
* flow with Google's raw error and nothing from us.
|
|
34
|
+
*/
|
|
35
|
+
readonly SSO_INCOMPLETE: "auth/sso-incomplete";
|
|
36
|
+
/** SSO failed for a reason we could not classify. */
|
|
37
|
+
readonly SSO_FAILED: "auth/sso-failed";
|
|
38
|
+
/**
|
|
39
|
+
* The WebAuthn ceremony ended without an assertion (AGL-1417).
|
|
40
|
+
*
|
|
41
|
+
* Deliberately NOT called "cancelled". The spec overloads
|
|
42
|
+
* `NotAllowedError` to cover a dismissed prompt, a timeout, AND no
|
|
43
|
+
* discoverable credential for the RP ID — the last on purpose, so a site
|
|
44
|
+
* cannot probe for a credential's existence. Naming this "cancelled" is
|
|
45
|
+
* what let "you have no passkey on this device" render as nothing at all.
|
|
46
|
+
*/
|
|
47
|
+
readonly PASSKEY_NOT_COMPLETED: "auth/passkey-not-completed";
|
|
48
|
+
/** The ceremony ran but the server refused the assertion. */
|
|
49
|
+
readonly PASSKEY_SIGNIN_FAILED: "auth/passkey-signin-failed";
|
|
50
|
+
};
|
|
51
|
+
export type AuthAppCode = (typeof AuthAppErrorCodes)[keyof typeof AuthAppErrorCodes];
|
|
52
|
+
export type AuthCode = IndexOf<typeof AuthErrorCodes> | AuthAppCode | 'general';
|
|
53
|
+
export type AuthResultError = AuthError;
|
|
54
|
+
export type AuthResultUser = UserCredential & {
|
|
55
|
+
credential?: OAuthCredential;
|
|
56
|
+
};
|
|
57
|
+
export type AuthCallbackResult = Promise<UserCredential>;
|
|
58
|
+
export declare const AuthErrorIgnore: {
|
|
59
|
+
"auth/user-cancelled": boolean;
|
|
60
|
+
"auth/redirect-cancelled-by-user": boolean;
|
|
61
|
+
"auth/popup-closed-by-user": boolean;
|
|
62
|
+
};
|
|
63
|
+
export declare const AuthErrorNotice: {
|
|
64
|
+
"auth/user-signed-out": boolean;
|
|
65
|
+
"auth/requires-recent-login": boolean;
|
|
66
|
+
"auth/sso-account-mismatch": boolean;
|
|
67
|
+
"auth/sso-not-configured": boolean;
|
|
68
|
+
"auth/sso-input-required": boolean;
|
|
69
|
+
"auth/sso-incomplete": boolean;
|
|
70
|
+
"auth/passkey-not-completed": boolean;
|
|
71
|
+
};
|
|
72
|
+
export declare const AuthErrorMessage: Partial<Record<AuthCode, string>>;
|
|
73
|
+
export declare const COOKIE_KEY_USER_TOKEN = "aglyn-user-token";
|