@gilav21/shadcn-angular-rte 0.1.2 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/README.md +43 -27
  2. package/fesm2022/{gilav21-shadcn-angular-rte-docx-parser-B8BeJWtY.mjs → gilav21-shadcn-angular-rte-docx-parser-DiBPweI4.mjs} +63 -38
  3. package/fesm2022/gilav21-shadcn-angular-rte-docx-parser-DiBPweI4.mjs.map +1 -0
  4. package/fesm2022/{gilav21-shadcn-angular-rte-docx-to-editor-html-Cl9eIhkC.mjs → gilav21-shadcn-angular-rte-docx-to-editor-html-BreagmS_.mjs} +43 -33
  5. package/fesm2022/gilav21-shadcn-angular-rte-docx-to-editor-html-BreagmS_.mjs.map +1 -0
  6. package/fesm2022/{gilav21-shadcn-angular-rte-gilav21-shadcn-angular-rte-CfjU_y-y.mjs → gilav21-shadcn-angular-rte-gilav21-shadcn-angular-rte-BnEAQOuU.mjs} +3561 -1901
  7. package/fesm2022/gilav21-shadcn-angular-rte-gilav21-shadcn-angular-rte-BnEAQOuU.mjs.map +1 -0
  8. package/fesm2022/gilav21-shadcn-angular-rte-markup-safety-qJk8EoPk.mjs +69 -0
  9. package/fesm2022/gilav21-shadcn-angular-rte-markup-safety-qJk8EoPk.mjs.map +1 -0
  10. package/fesm2022/{gilav21-shadcn-angular-rte-pdf-readable-DmE-CIxv.mjs → gilav21-shadcn-angular-rte-pdf-readable-JvCpuNQH.mjs} +445 -124
  11. package/fesm2022/gilav21-shadcn-angular-rte-pdf-readable-JvCpuNQH.mjs.map +1 -0
  12. package/fesm2022/gilav21-shadcn-angular-rte.mjs +1 -1
  13. package/package.json +6 -2
  14. package/schematics/collection.json +10 -0
  15. package/schematics/ng-add/index.cjs +169 -0
  16. package/schematics/ng-add/schema.json +13 -0
  17. package/styles.css +2 -0
  18. package/types/gilav21-shadcn-angular-rte.d.ts +365 -114
  19. package/types/gilav21-shadcn-angular-rte.d.ts.map +1 -1
  20. package/fesm2022/gilav21-shadcn-angular-rte-docx-parser-B8BeJWtY.mjs.map +0 -1
  21. package/fesm2022/gilav21-shadcn-angular-rte-docx-to-editor-html-Cl9eIhkC.mjs.map +0 -1
  22. package/fesm2022/gilav21-shadcn-angular-rte-gilav21-shadcn-angular-rte-CfjU_y-y.mjs.map +0 -1
  23. package/fesm2022/gilav21-shadcn-angular-rte-pdf-readable-DmE-CIxv.mjs.map +0 -1
  24. package/theme.css +0 -159
package/README.md CHANGED
@@ -12,39 +12,54 @@ fixes.
12
12
  - **Angular 20 or 21** (peer `>=20.0.0 <22.0.0`). Both majors are covered by
13
13
  the e2e suite, which installs this package's real tarball into a pristine
14
14
  Angular 20 app and a pristine Angular 21 app and builds both.
15
- - **Tailwind CSS v4**.
16
15
  - Works in zoneless apps.
16
+ - **No Tailwind required.** The package ships its styles precompiled; your app
17
+ does not need Tailwind, PostCSS or any CSS setup of its own.
17
18
 
18
19
  ## Install
19
20
 
20
21
  ```bash
21
- npm install @gilav21/shadcn-angular-rte
22
- npm install -D tailwindcss @tailwindcss/postcss postcss
22
+ ng add @gilav21/shadcn-angular-rte
23
23
  ```
24
24
 
25
- If you do not already have Tailwind wired up, add `.postcssrc.json`:
25
+ That installs the package and registers its stylesheet in `angular.json`.
26
+ Nothing else to configure.
27
+
28
+ Can't run `ng add`? Install with npm and add the stylesheet to the `styles`
29
+ array of your app's build target yourself:
26
30
 
27
31
  ```json
28
- { "plugins": { "@tailwindcss/postcss": {} } }
32
+ "styles": ["@gilav21/shadcn-angular-rte/styles.css", "src/styles.css"]
29
33
  ```
30
34
 
31
- ## Styles
35
+ ### What the stylesheet does — and doesn't — touch
36
+
37
+ `styles.css` styles only the editor and its overlays. It does **not** restyle your app:
38
+
39
+ - Tailwind's reset is scoped to the package's own elements — your `body`,
40
+ headings and buttons keep their styles.
41
+ - The design tokens (`--primary`, `--background`, …) are declared at zero
42
+ specificity, so tokens your app already defines — for example from
43
+ `npx shadcn-angular init` — win.
44
+ - Apps that run their own Tailwind build are fine: the package's utilities
45
+ coexist with yours.
46
+ - Text uses **your app's font** — the package sets no `font-family` of its own,
47
+ so it matches your typography. An app with no font set shows the browser
48
+ default (usually a serif); set one on `body` (for example
49
+ `font-family: system-ui, sans-serif`) for the familiar shadcn look.
50
+
51
+ ## Themes
32
52
 
33
- Add these three lines to your global stylesheet:
53
+ `ui-rich-text-editor` takes a `theme` input with the same presets `npx shadcn-angular
54
+ change-theme` offers: `zinc`, `slate`, `stone`, `gray`, `neutral`, `red`, `rose`, `orange`, `green`, `blue`, `yellow`, `violet`, `amber`.
34
55
 
35
- ```css
36
- @import "tailwindcss";
37
- @source "../node_modules/@gilav21/shadcn-angular-rte";
38
- @import "@gilav21/shadcn-angular-rte/theme.css";
56
+ ```html
57
+ <ui-rich-text-editor theme="violet" … />
39
58
  ```
40
59
 
41
- - The `@source` line is **required**. Tailwind v4 does not scan `node_modules`
42
- by default, so without it none of the editor's utility classes are generated
43
- and the editor renders unstyled.
44
- - The `theme.css` import ships the design tokens (`:root` / `.dark` custom
45
- properties, `@theme inline`, base layer). **Skip it if your app is already
46
- CLI-initialised** (`npx shadcn-angular init` wrote the same tokens into your
47
- own stylesheet, and importing both duplicates the `:root` block).
60
+ The preset applies to that instance only, including its menus, popovers,
61
+ tooltips and dialogs, and it follows `.dark` mode. Leave `theme` unset to
62
+ use your app's tokens.
48
63
 
49
64
  ## Usage
50
65
 
@@ -120,9 +135,9 @@ download only when a user actually imports a file.
120
135
  ## Locale, RTL and density
121
136
 
122
137
  `[locale]` is set per editor instance (`locale="he"`), and RTL follows the
123
- document direction. Density is driven by the CSS custom properties in
124
- `theme.css`; override `--density` (or `--density-rich-text-editor`) in your own
125
- stylesheet to scale spacing.
138
+ document direction. Density is driven by CSS custom properties; override
139
+ `--density` (or `--density-rich-text-editor`) in your own stylesheet to scale
140
+ spacing.
126
141
 
127
142
  ## Selectors are fixed
128
143
 
@@ -152,13 +167,14 @@ uses. In practice:
152
167
 
153
168
  ## Troubleshooting
154
169
 
155
- **Nothing is styled.** The `@source` line is missing or points at the wrong
156
- path. It must resolve, from the stylesheet's own location, to this package
157
- inside `node_modules`.
170
+ **Nothing is styled.** The stylesheet is not registered. Re-run `ng add @gilav21/shadcn-angular-rte`,
171
+ or check that `"@gilav21/shadcn-angular-rte/styles.css"` is in your build target's `styles` array.
158
172
 
159
- **Colours are missing / everything is transparent.** `theme.css` was not
160
- imported and your app has no shadcn token block of its own. Add the third CSS
161
- line.
173
+ **A package element looks off in my app.** Global rules your app declares
174
+ outside any `@layer` (say `button { padding: 1rem }`) beat the package's
175
+ styles, which live in cascade layers. Scope such rules to your own markup, or
176
+ put your global stylesheet in a layer. To restyle a package element on purpose,
177
+ pass classes through its `class` input.
162
178
 
163
179
  **`npm install` fails with a peer error on `@angular/core`.** This package
164
180
  supports Angular 20 and 21. On Angular 19 or older, use the CLI copy model
@@ -1,5 +1,5 @@
1
1
  import { i as inflate } from './gilav21-shadcn-angular-rte-inflate-laXxypva.mjs';
2
- import { i as isValidImageMagicBytes } from './gilav21-shadcn-angular-rte-gilav21-shadcn-angular-rte-CfjU_y-y.mjs';
2
+ import { i as isValidImageMagicBytes } from './gilav21-shadcn-angular-rte-gilav21-shadcn-angular-rte-BnEAQOuU.mjs';
3
3
 
4
4
  const DEFAULT_MAX_UNCOMPRESSED = 1024 * 1024 * 1024;
5
5
  const DEFAULT_MAX_FILE_SIZE = 200 * 1024 * 1024;
@@ -279,6 +279,31 @@ const HIGHLIGHT_COLOR_MAP = {
279
279
  black: '#000000',
280
280
  white: '#FFFFFF',
281
281
  };
282
+ /**
283
+ * OOXML `ST_HexColor`: exactly six hex digits (`auto` and anything else is not a
284
+ * colour). The model's colour fields hold only this form, so a value read out of
285
+ * the document can never carry a quote, a `;` or a `url(` into the markup.
286
+ */
287
+ function hexColor(raw) {
288
+ return raw && /^[\dA-Fa-f]{6}$/.test(raw) ? `#${raw}` : undefined;
289
+ }
290
+ const TEXT_ALIGN_BY_JC = new Map([
291
+ ['both', 'justify'], ['distribute', 'justify'],
292
+ ['center', 'center'],
293
+ ['left', 'left'], ['start', 'left'],
294
+ ['right', 'right'], ['end', 'right'],
295
+ ]);
296
+ const VERTICAL_ALIGN_BY_VALIGN = new Map([
297
+ ['top', 'top'], ['center', 'middle'], ['bottom', 'bottom'],
298
+ ]);
299
+ /** The CSS `text-align` keyword for an OOXML `w:jc` value, or `undefined` for a value outside the vocabulary. */
300
+ function textAlignFromJc(jc) {
301
+ return TEXT_ALIGN_BY_JC.get(jc);
302
+ }
303
+ /** The CSS `vertical-align` keyword for an OOXML `w:vAlign` value, or `undefined` for a value outside the vocabulary. */
304
+ function verticalAlignFromVAlign(vAlign) {
305
+ return VERTICAL_ALIGN_BY_VALIGN.get(vAlign);
306
+ }
282
307
  function parseXml(xmlString) {
283
308
  const parser = new DOMParser();
284
309
  return parser.parseFromString(xmlString, 'application/xml');
@@ -393,7 +418,7 @@ function parseFontSizeAndColor(rPr, result, themeColors) {
393
418
  function parseColorElement(color, themeColors) {
394
419
  const val = getAttrVal(color, NS_W, 'val');
395
420
  if (val && val !== 'auto')
396
- return `#${val}`;
421
+ return hexColor(val);
397
422
  if (!themeColors)
398
423
  return undefined;
399
424
  const themeColor = getAttrVal(color, NS_W, 'themeColor');
@@ -416,16 +441,20 @@ function applyTintShade(hex, tint, shade) {
416
441
  const r = Number.parseInt(hex.substring(1, 3), 16);
417
442
  const g = Number.parseInt(hex.substring(3, 5), 16);
418
443
  const b = Number.parseInt(hex.substring(5, 7), 16);
419
- if (tint) {
420
- const t = Number.parseInt(tint, 16) / 255;
444
+ const t = tint ? hexFraction(tint) : undefined;
445
+ if (t !== undefined) {
421
446
  return rgbToHex(Math.round(r + (255 - r) * t), Math.round(g + (255 - g) * t), Math.round(b + (255 - b) * t));
422
447
  }
423
- if (shade) {
424
- const s = Number.parseInt(shade, 16) / 255;
448
+ const s = shade ? hexFraction(shade) : undefined;
449
+ if (s !== undefined) {
425
450
  return rgbToHex(Math.round(r * s), Math.round(g * s), Math.round(b * s));
426
451
  }
427
452
  return hex;
428
453
  }
454
+ /** `00`-`FF` as 0-1; `undefined` for a value that is not hex, so a bad tint leaves the colour as it was. */
455
+ function hexFraction(value) {
456
+ return /^[\dA-Fa-f]{1,2}$/.test(value) ? Number.parseInt(value, 16) / 255 : undefined;
457
+ }
429
458
  function rgbToHex(r, g, b) {
430
459
  const toHex = (v) => Math.min(255, Math.max(0, v)).toString(16).padStart(2, '0');
431
460
  return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
@@ -452,15 +481,16 @@ function parseHighlightAndShading(rPr, result) {
452
481
  const highlightEl = getChildNS(rPr, NS_W, 'highlight');
453
482
  if (highlightEl) {
454
483
  const val = getAttrVal(highlightEl, NS_W, 'val');
455
- if (val && val !== 'none') {
456
- result.highlight = HIGHLIGHT_COLOR_MAP[val] ?? `#${val}`;
457
- }
484
+ // An own-property lookup: `constructor` and `__proto__` are not highlight names.
485
+ const highlight = val && Object.hasOwn(HIGHLIGHT_COLOR_MAP, val) ? HIGHLIGHT_COLOR_MAP[val] : hexColor(val);
486
+ if (highlight)
487
+ result.highlight = highlight;
458
488
  }
459
489
  const shd = getChildNS(rPr, NS_W, 'shd');
460
490
  if (shd) {
461
- const fill = getAttrVal(shd, NS_W, 'fill');
462
- if (fill && fill !== 'auto')
463
- result.backgroundColor = `#${fill}`;
491
+ const fill = hexColor(getAttrVal(shd, NS_W, 'fill'));
492
+ if (fill)
493
+ result.backgroundColor = fill;
464
494
  }
465
495
  }
466
496
  function parseVertAlignAndRtl(rPr, result) {
@@ -549,9 +579,9 @@ function parseParagraphBordersAndShading(pPr) {
549
579
  const result = {};
550
580
  const shd = getChildNS(pPr, NS_W, 'shd');
551
581
  if (shd) {
552
- const fill = getAttrVal(shd, NS_W, 'fill');
553
- if (fill && fill !== 'auto')
554
- result.shading = `#${fill}`;
582
+ const fill = hexColor(getAttrVal(shd, NS_W, 'fill'));
583
+ if (fill)
584
+ result.shading = fill;
555
585
  }
556
586
  const pBdr = getChildNS(pPr, NS_W, 'pBdr');
557
587
  if (pBdr) {
@@ -577,8 +607,7 @@ function parseBorderSet(borderParent) {
577
607
  }
578
608
  function parseSingleBorder(el) {
579
609
  const style = getAttrVal(el, NS_W, 'val') ?? 'single';
580
- const colorVal = getAttrVal(el, NS_W, 'color');
581
- const color = colorVal && colorVal !== 'auto' ? `#${colorVal}` : '#000000';
610
+ const color = hexColor(getAttrVal(el, NS_W, 'color')) ?? '#000000';
582
611
  const sizeVal = getAttrVal(el, NS_W, 'sz');
583
612
  const size = sizeVal ? Number.parseInt(sizeVal, 10) / 8 : 1;
584
613
  return { style, color, size };
@@ -616,9 +645,9 @@ function parseBidiAndAlignment(pPr) {
616
645
  }
617
646
  const jcEl = getChildNS(pPr, NS_W, 'jc');
618
647
  if (jcEl) {
619
- const val = getAttrVal(jcEl, NS_W, 'val');
620
- if (val)
621
- result.alignment = val;
648
+ const alignment = textAlignFromJc(getAttrVal(jcEl, NS_W, 'val') ?? '');
649
+ if (alignment)
650
+ result.alignment = alignment;
622
651
  }
623
652
  return result;
624
653
  }
@@ -968,17 +997,17 @@ function buildCellStyle(tcPr) {
968
997
  let hasStyle = false;
969
998
  const shd = getChildNS(tcPr, NS_W, 'shd');
970
999
  if (shd) {
971
- const fill = getAttrVal(shd, NS_W, 'fill');
972
- if (fill && fill !== 'auto') {
973
- style.backgroundColor = `#${fill}`;
1000
+ const fill = hexColor(getAttrVal(shd, NS_W, 'fill'));
1001
+ if (fill) {
1002
+ style.backgroundColor = fill;
974
1003
  hasStyle = true;
975
1004
  }
976
1005
  }
977
1006
  const vAlign = getChildNS(tcPr, NS_W, 'vAlign');
978
1007
  if (vAlign) {
979
- const val = getAttrVal(vAlign, NS_W, 'val');
980
- if (val) {
981
- style.verticalAlign = val;
1008
+ const verticalAlign = verticalAlignFromVAlign(getAttrVal(vAlign, NS_W, 'val') ?? '');
1009
+ if (verticalAlign) {
1010
+ style.verticalAlign = verticalAlign;
982
1011
  hasStyle = true;
983
1012
  }
984
1013
  }
@@ -1225,14 +1254,10 @@ function parseThemeColors(files) {
1225
1254
  if (!el)
1226
1255
  continue;
1227
1256
  const srgb = getChildNS(el, NS_A, 'srgbClr');
1228
- if (srgb) {
1229
- map.set(name, `#${srgb.getAttribute('val') ?? '000000'}`);
1230
- continue;
1231
- }
1232
1257
  const sys = getChildNS(el, NS_A, 'sysClr');
1233
- if (sys) {
1234
- map.set(name, `#${sys.getAttribute('lastClr') ?? sys.getAttribute('val') ?? '000000'}`);
1235
- }
1258
+ const hex = hexColor(srgb ? srgb.getAttribute('val') : sys?.getAttribute('lastClr'));
1259
+ if (hex)
1260
+ map.set(name, hex);
1236
1261
  }
1237
1262
  return map;
1238
1263
  }
@@ -1370,9 +1395,9 @@ function extractParagraphStyleProps(pPr) {
1370
1395
  const result = {};
1371
1396
  const jcEl = getChildNS(pPr, NS_W, 'jc');
1372
1397
  if (jcEl) {
1373
- const val = getAttrVal(jcEl, NS_W, 'val');
1374
- if (val)
1375
- result.alignment = val;
1398
+ const alignment = textAlignFromJc(getAttrVal(jcEl, NS_W, 'val') ?? '');
1399
+ if (alignment)
1400
+ result.alignment = alignment;
1376
1401
  }
1377
1402
  extractSpacingProps(pPr, result);
1378
1403
  extractIndentProps(pPr, result);
@@ -1524,5 +1549,5 @@ function parseDocx(data) {
1524
1549
  return { elements, plainText, footnotes, endnotes, comments, headers, footers };
1525
1550
  }
1526
1551
 
1527
- export { parseDocx };
1528
- //# sourceMappingURL=gilav21-shadcn-angular-rte-docx-parser-B8BeJWtY.mjs.map
1552
+ export { parseDocx, textAlignFromJc, verticalAlignFromVAlign };
1553
+ //# sourceMappingURL=gilav21-shadcn-angular-rte-docx-parser-DiBPweI4.mjs.map