@momentumcms/admin 0.1.9 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/fesm2022/momentumcms-admin-array-field.component-CT5NlIEv.mjs +316 -0
  2. package/fesm2022/momentumcms-admin-array-field.component-CT5NlIEv.mjs.map +1 -0
  3. package/fesm2022/momentumcms-admin-blocks-field.component-Cz7HmuBK.mjs +362 -0
  4. package/fesm2022/momentumcms-admin-blocks-field.component-Cz7HmuBK.mjs.map +1 -0
  5. package/fesm2022/momentumcms-admin-collapsible-field.component-CtwrGQvg.mjs +120 -0
  6. package/fesm2022/momentumcms-admin-collapsible-field.component-CtwrGQvg.mjs.map +1 -0
  7. package/fesm2022/{momentumcms-admin-global-edit.page-DFDV-uh3.mjs → momentumcms-admin-global-edit.page-BBUtWCSl.mjs} +2 -2
  8. package/fesm2022/{momentumcms-admin-global-edit.page-DFDV-uh3.mjs.map → momentumcms-admin-global-edit.page-BBUtWCSl.mjs.map} +1 -1
  9. package/fesm2022/momentumcms-admin-group-field.component-BZeG8Oqy.mjs +184 -0
  10. package/fesm2022/momentumcms-admin-group-field.component-BZeG8Oqy.mjs.map +1 -0
  11. package/fesm2022/{momentumcms-admin-momentumcms-admin-z82jXEsP.mjs → momentumcms-admin-momentumcms-admin-o0FbJXZN.mjs} +9688 -12241
  12. package/fesm2022/momentumcms-admin-momentumcms-admin-o0FbJXZN.mjs.map +1 -0
  13. package/fesm2022/momentumcms-admin-relationship-field.component-BuxtRs2_.mjs +473 -0
  14. package/fesm2022/momentumcms-admin-relationship-field.component-BuxtRs2_.mjs.map +1 -0
  15. package/fesm2022/momentumcms-admin-rich-text-field.component-DKQ6pwp7.mjs +813 -0
  16. package/fesm2022/momentumcms-admin-rich-text-field.component-DKQ6pwp7.mjs.map +1 -0
  17. package/fesm2022/momentumcms-admin-row-field.component-ks3FXd4B.mjs +98 -0
  18. package/fesm2022/momentumcms-admin-row-field.component-ks3FXd4B.mjs.map +1 -0
  19. package/fesm2022/momentumcms-admin-tabs-field.component-mZ4dpZoD.mjs +189 -0
  20. package/fesm2022/momentumcms-admin-tabs-field.component-mZ4dpZoD.mjs.map +1 -0
  21. package/fesm2022/momentumcms-admin.mjs +1 -1
  22. package/package.json +1 -1
  23. package/types/momentumcms-admin.d.ts +79 -13
  24. package/fesm2022/momentumcms-admin-momentumcms-admin-z82jXEsP.mjs.map +0 -1
@@ -0,0 +1,813 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, DestroyRef, input, computed, viewChild, signal, afterNextRender, effect, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { McmsFormField } from '@momentumcms/ui';
4
+ import { humanizeFieldName } from '@momentumcms/core';
5
+ import { a as getFieldNodeState } from './momentumcms-admin-momentumcms-admin-o0FbJXZN.mjs';
6
+ import { Editor } from '@tiptap/core';
7
+ import StarterKit from '@tiptap/starter-kit';
8
+ import Underline from '@tiptap/extension-underline';
9
+ import Link from '@tiptap/extension-link';
10
+ import Placeholder from '@tiptap/extension-placeholder';
11
+
12
+ /**
13
+ * Rich text field renderer using TipTap editor.
14
+ * Provides a toolbar with formatting options and stores content as HTML.
15
+ *
16
+ * Uses Angular Signal Forms bridge pattern: reads/writes value via
17
+ * a FieldTree node's FieldState rather than event-based I/O.
18
+ */
19
+ class RichTextFieldRenderer {
20
+ destroyRef = inject(DestroyRef);
21
+ /** Field definition */
22
+ field = input.required(...(ngDevMode ? [{ debugName: "field" }] : []));
23
+ /** Signal forms FieldTree node for this field */
24
+ formNode = input(null, ...(ngDevMode ? [{ debugName: "formNode" }] : []));
25
+ /** Form mode */
26
+ mode = input('create', ...(ngDevMode ? [{ debugName: "mode" }] : []));
27
+ /** Field path */
28
+ path = input.required(...(ngDevMode ? [{ debugName: "path" }] : []));
29
+ /** Bridge: extract FieldState from formNode */
30
+ nodeState = computed(() => getFieldNodeState(this.formNode()), ...(ngDevMode ? [{ debugName: "nodeState" }] : []));
31
+ /** Editor container element */
32
+ editorRef = viewChild('editorElement', ...(ngDevMode ? [{ debugName: "editorRef" }] : []));
33
+ /** TipTap editor instance */
34
+ editor = null;
35
+ /** Whether we're currently updating from external value (prevents feedback loops) */
36
+ updatingFromExternal = false;
37
+ /** Whether the editor has been initialized (browser only) */
38
+ editorReady = signal(false, ...(ngDevMode ? [{ debugName: "editorReady" }] : []));
39
+ /** Toolbar state signals */
40
+ isBold = signal(false, ...(ngDevMode ? [{ debugName: "isBold" }] : []));
41
+ isItalic = signal(false, ...(ngDevMode ? [{ debugName: "isItalic" }] : []));
42
+ isUnderline = signal(false, ...(ngDevMode ? [{ debugName: "isUnderline" }] : []));
43
+ isStrike = signal(false, ...(ngDevMode ? [{ debugName: "isStrike" }] : []));
44
+ isHeading1 = signal(false, ...(ngDevMode ? [{ debugName: "isHeading1" }] : []));
45
+ isHeading2 = signal(false, ...(ngDevMode ? [{ debugName: "isHeading2" }] : []));
46
+ isHeading3 = signal(false, ...(ngDevMode ? [{ debugName: "isHeading3" }] : []));
47
+ isBulletList = signal(false, ...(ngDevMode ? [{ debugName: "isBulletList" }] : []));
48
+ isOrderedList = signal(false, ...(ngDevMode ? [{ debugName: "isOrderedList" }] : []));
49
+ isBlockquote = signal(false, ...(ngDevMode ? [{ debugName: "isBlockquote" }] : []));
50
+ isCodeBlock = signal(false, ...(ngDevMode ? [{ debugName: "isCodeBlock" }] : []));
51
+ /** Unique field ID */
52
+ fieldId = computed(() => `field-${this.path().replace(/\./g, '-')}`, ...(ngDevMode ? [{ debugName: "fieldId" }] : []));
53
+ /** Computed label */
54
+ label = computed(() => this.field().label || humanizeFieldName(this.field().name), ...(ngDevMode ? [{ debugName: "label" }] : []));
55
+ /** Whether the field is required */
56
+ required = computed(() => this.field().required ?? false, ...(ngDevMode ? [{ debugName: "required" }] : []));
57
+ /** Whether the field is disabled */
58
+ isDisabled = computed(() => {
59
+ return this.mode() === 'view' || (this.field().admin?.readOnly ?? false);
60
+ }, ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
61
+ /** String value from FieldState */
62
+ stringValue = computed(() => {
63
+ const state = this.nodeState();
64
+ if (!state)
65
+ return '';
66
+ const val = state.value();
67
+ return val === null || val === undefined ? '' : String(val);
68
+ }, ...(ngDevMode ? [{ debugName: "stringValue" }] : []));
69
+ /** Validation errors shown only when field is touched */
70
+ touchedErrors = computed(() => {
71
+ const state = this.nodeState();
72
+ if (!state || !state.touched())
73
+ return [];
74
+ return state.errors().map((e) => ({ kind: e.kind, message: e.message }));
75
+ }, ...(ngDevMode ? [{ debugName: "touchedErrors" }] : []));
76
+ constructor() {
77
+ // Mount editor after first browser render (SSR-safe).
78
+ afterNextRender(() => {
79
+ this.mountEditor();
80
+ });
81
+ // Sync external value changes into the editor
82
+ effect(() => {
83
+ const val = this.stringValue();
84
+ if (this.editor && !this.editor.isDestroyed) {
85
+ const currentHtml = this.editor.getHTML();
86
+ if (val !== currentHtml && !(val === '' && currentHtml === '<p></p>')) {
87
+ this.updatingFromExternal = true;
88
+ this.editor.commands.setContent(val || '', { emitUpdate: false });
89
+ this.updatingFromExternal = false;
90
+ }
91
+ }
92
+ });
93
+ this.destroyRef.onDestroy(() => {
94
+ this.editor?.destroy();
95
+ this.editor = null;
96
+ });
97
+ }
98
+ /**
99
+ * Handle blur from editor.
100
+ */
101
+ onBlur() {
102
+ const state = this.nodeState();
103
+ if (state)
104
+ state.markAsTouched();
105
+ }
106
+ mountEditor() {
107
+ const el = this.editorRef();
108
+ if (!el || this.isDisabled() || this.editor)
109
+ return;
110
+ this.editor = new Editor({
111
+ element: el.nativeElement,
112
+ editorProps: {
113
+ attributes: {
114
+ role: 'textbox',
115
+ 'aria-multiline': 'true',
116
+ 'aria-label': this.label() + ' editor',
117
+ },
118
+ },
119
+ extensions: [
120
+ StarterKit.configure({
121
+ heading: { levels: [1, 2, 3] },
122
+ }),
123
+ Underline,
124
+ Link.configure({
125
+ openOnClick: false,
126
+ HTMLAttributes: {
127
+ rel: 'noopener noreferrer nofollow',
128
+ },
129
+ }),
130
+ Placeholder.configure({
131
+ placeholder: this.field().admin?.placeholder || 'Start writing...',
132
+ }),
133
+ ],
134
+ content: this.stringValue() || '',
135
+ editable: true,
136
+ onUpdate: ({ editor }) => {
137
+ if (!this.updatingFromExternal) {
138
+ const html = editor.getHTML();
139
+ const value = html === '<p></p>' ? '' : html;
140
+ const state = this.nodeState();
141
+ if (state)
142
+ state.value.set(value);
143
+ }
144
+ },
145
+ onSelectionUpdate: ({ editor }) => {
146
+ this.updateToolbarState(editor);
147
+ },
148
+ onTransaction: ({ editor }) => {
149
+ this.updateToolbarState(editor);
150
+ },
151
+ onBlur: () => {
152
+ this.onBlur();
153
+ },
154
+ });
155
+ this.editorReady.set(true);
156
+ }
157
+ updateToolbarState(editor) {
158
+ this.isBold.set(editor.isActive('bold'));
159
+ this.isItalic.set(editor.isActive('italic'));
160
+ this.isUnderline.set(editor.isActive('underline'));
161
+ this.isStrike.set(editor.isActive('strike'));
162
+ this.isHeading1.set(editor.isActive('heading', { level: 1 }));
163
+ this.isHeading2.set(editor.isActive('heading', { level: 2 }));
164
+ this.isHeading3.set(editor.isActive('heading', { level: 3 }));
165
+ this.isBulletList.set(editor.isActive('bulletList'));
166
+ this.isOrderedList.set(editor.isActive('orderedList'));
167
+ this.isBlockquote.set(editor.isActive('blockquote'));
168
+ this.isCodeBlock.set(editor.isActive('codeBlock'));
169
+ }
170
+ toggleBold() {
171
+ this.editor?.chain().focus().toggleBold().run();
172
+ }
173
+ toggleItalic() {
174
+ this.editor?.chain().focus().toggleItalic().run();
175
+ }
176
+ toggleUnderline() {
177
+ this.editor?.chain().focus().toggleUnderline().run();
178
+ }
179
+ toggleStrike() {
180
+ this.editor?.chain().focus().toggleStrike().run();
181
+ }
182
+ toggleHeading(level) {
183
+ this.editor?.chain().focus().toggleHeading({ level }).run();
184
+ }
185
+ toggleBulletList() {
186
+ this.editor?.chain().focus().toggleBulletList().run();
187
+ }
188
+ toggleOrderedList() {
189
+ this.editor?.chain().focus().toggleOrderedList().run();
190
+ }
191
+ toggleBlockquote() {
192
+ this.editor?.chain().focus().toggleBlockquote().run();
193
+ }
194
+ toggleCodeBlock() {
195
+ this.editor?.chain().focus().toggleCodeBlock().run();
196
+ }
197
+ insertHorizontalRule() {
198
+ this.editor?.chain().focus().setHorizontalRule().run();
199
+ }
200
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: RichTextFieldRenderer, deps: [], target: i0.ɵɵFactoryTarget.Component });
201
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: RichTextFieldRenderer, isStandalone: true, selector: "mcms-rich-text-field-renderer", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, formNode: { classPropertyName: "formNode", publicName: "formNode", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, path: { classPropertyName: "path", publicName: "path", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "block" }, viewQueries: [{ propertyName: "editorRef", first: true, predicate: ["editorElement"], descendants: true, isSignal: true }], ngImport: i0, template: `
202
+ <mcms-form-field
203
+ [id]="fieldId()"
204
+ [required]="required()"
205
+ [disabled]="isDisabled()"
206
+ [errors]="touchedErrors()"
207
+ >
208
+ <span mcmsLabel>{{ label() }}</span>
209
+
210
+ @if (!isDisabled()) {
211
+ <div
212
+ class="rounded-md border border-input bg-background focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2"
213
+ >
214
+ <!-- Toolbar -->
215
+ <div
216
+ class="flex flex-wrap items-center gap-0.5 border-b border-input px-1 py-1"
217
+ role="toolbar"
218
+ [attr.aria-label]="label() + ' formatting'"
219
+ >
220
+ <button
221
+ type="button"
222
+ class="inline-flex h-8 w-8 items-center justify-center rounded text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground"
223
+ [class.bg-accent]="isBold()"
224
+ [class.text-accent-foreground]="isBold()"
225
+ title="Bold (Ctrl+B)"
226
+ aria-label="Bold"
227
+ [attr.aria-pressed]="isBold()"
228
+ (click)="toggleBold()"
229
+ >
230
+ <svg
231
+ aria-hidden="true"
232
+ class="h-4 w-4"
233
+ viewBox="0 0 24 24"
234
+ fill="none"
235
+ stroke="currentColor"
236
+ stroke-width="3"
237
+ stroke-linecap="round"
238
+ stroke-linejoin="round"
239
+ >
240
+ <path d="M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z" />
241
+ <path d="M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z" />
242
+ </svg>
243
+ </button>
244
+
245
+ <button
246
+ type="button"
247
+ class="inline-flex h-8 w-8 items-center justify-center rounded text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground"
248
+ [class.bg-accent]="isItalic()"
249
+ [class.text-accent-foreground]="isItalic()"
250
+ title="Italic (Ctrl+I)"
251
+ aria-label="Italic"
252
+ [attr.aria-pressed]="isItalic()"
253
+ (click)="toggleItalic()"
254
+ >
255
+ <svg
256
+ aria-hidden="true"
257
+ class="h-4 w-4"
258
+ viewBox="0 0 24 24"
259
+ fill="none"
260
+ stroke="currentColor"
261
+ stroke-width="2"
262
+ stroke-linecap="round"
263
+ stroke-linejoin="round"
264
+ >
265
+ <line x1="19" y1="4" x2="10" y2="4" />
266
+ <line x1="14" y1="20" x2="5" y2="20" />
267
+ <line x1="15" y1="4" x2="9" y2="20" />
268
+ </svg>
269
+ </button>
270
+
271
+ <button
272
+ type="button"
273
+ class="inline-flex h-8 w-8 items-center justify-center rounded text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground"
274
+ [class.bg-accent]="isUnderline()"
275
+ [class.text-accent-foreground]="isUnderline()"
276
+ title="Underline (Ctrl+U)"
277
+ aria-label="Underline"
278
+ [attr.aria-pressed]="isUnderline()"
279
+ (click)="toggleUnderline()"
280
+ >
281
+ <svg
282
+ aria-hidden="true"
283
+ class="h-4 w-4"
284
+ viewBox="0 0 24 24"
285
+ fill="none"
286
+ stroke="currentColor"
287
+ stroke-width="2"
288
+ stroke-linecap="round"
289
+ stroke-linejoin="round"
290
+ >
291
+ <path d="M6 3v7a6 6 0 0 0 6 6 6 6 0 0 0 6-6V3" />
292
+ <line x1="4" y1="21" x2="20" y2="21" />
293
+ </svg>
294
+ </button>
295
+
296
+ <button
297
+ type="button"
298
+ class="inline-flex h-8 w-8 items-center justify-center rounded text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground"
299
+ [class.bg-accent]="isStrike()"
300
+ [class.text-accent-foreground]="isStrike()"
301
+ title="Strikethrough"
302
+ aria-label="Strikethrough"
303
+ [attr.aria-pressed]="isStrike()"
304
+ (click)="toggleStrike()"
305
+ >
306
+ <svg
307
+ aria-hidden="true"
308
+ class="h-4 w-4"
309
+ viewBox="0 0 24 24"
310
+ fill="none"
311
+ stroke="currentColor"
312
+ stroke-width="2"
313
+ stroke-linecap="round"
314
+ stroke-linejoin="round"
315
+ >
316
+ <path d="M16 4H9a3 3 0 0 0-2.83 4" />
317
+ <path d="M14 12a4 4 0 0 1 0 8H6" />
318
+ <line x1="4" y1="12" x2="20" y2="12" />
319
+ </svg>
320
+ </button>
321
+
322
+ <div class="mx-1 h-6 w-px bg-border" role="separator"></div>
323
+
324
+ <button
325
+ type="button"
326
+ class="inline-flex h-8 w-8 items-center justify-center rounded text-sm font-bold transition-colors hover:bg-accent hover:text-accent-foreground"
327
+ [class.bg-accent]="isHeading1()"
328
+ [class.text-accent-foreground]="isHeading1()"
329
+ title="Heading 1"
330
+ aria-label="Heading 1"
331
+ [attr.aria-pressed]="isHeading1()"
332
+ (click)="toggleHeading(1)"
333
+ >
334
+ H1
335
+ </button>
336
+
337
+ <button
338
+ type="button"
339
+ class="inline-flex h-8 w-8 items-center justify-center rounded text-sm font-bold transition-colors hover:bg-accent hover:text-accent-foreground"
340
+ [class.bg-accent]="isHeading2()"
341
+ [class.text-accent-foreground]="isHeading2()"
342
+ title="Heading 2"
343
+ aria-label="Heading 2"
344
+ [attr.aria-pressed]="isHeading2()"
345
+ (click)="toggleHeading(2)"
346
+ >
347
+ H2
348
+ </button>
349
+
350
+ <button
351
+ type="button"
352
+ class="inline-flex h-8 w-8 items-center justify-center rounded text-sm font-bold transition-colors hover:bg-accent hover:text-accent-foreground"
353
+ [class.bg-accent]="isHeading3()"
354
+ [class.text-accent-foreground]="isHeading3()"
355
+ title="Heading 3"
356
+ aria-label="Heading 3"
357
+ [attr.aria-pressed]="isHeading3()"
358
+ (click)="toggleHeading(3)"
359
+ >
360
+ H3
361
+ </button>
362
+
363
+ <div class="mx-1 h-6 w-px bg-border" role="separator"></div>
364
+
365
+ <button
366
+ type="button"
367
+ class="inline-flex h-8 w-8 items-center justify-center rounded text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground"
368
+ [class.bg-accent]="isBulletList()"
369
+ [class.text-accent-foreground]="isBulletList()"
370
+ title="Bullet List"
371
+ aria-label="Bullet list"
372
+ [attr.aria-pressed]="isBulletList()"
373
+ (click)="toggleBulletList()"
374
+ >
375
+ <svg
376
+ aria-hidden="true"
377
+ class="h-4 w-4"
378
+ viewBox="0 0 24 24"
379
+ fill="none"
380
+ stroke="currentColor"
381
+ stroke-width="2"
382
+ stroke-linecap="round"
383
+ stroke-linejoin="round"
384
+ >
385
+ <line x1="8" y1="6" x2="21" y2="6" />
386
+ <line x1="8" y1="12" x2="21" y2="12" />
387
+ <line x1="8" y1="18" x2="21" y2="18" />
388
+ <line x1="3" y1="6" x2="3.01" y2="6" />
389
+ <line x1="3" y1="12" x2="3.01" y2="12" />
390
+ <line x1="3" y1="18" x2="3.01" y2="18" />
391
+ </svg>
392
+ </button>
393
+
394
+ <button
395
+ type="button"
396
+ class="inline-flex h-8 w-8 items-center justify-center rounded text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground"
397
+ [class.bg-accent]="isOrderedList()"
398
+ [class.text-accent-foreground]="isOrderedList()"
399
+ title="Numbered List"
400
+ aria-label="Numbered list"
401
+ [attr.aria-pressed]="isOrderedList()"
402
+ (click)="toggleOrderedList()"
403
+ >
404
+ <svg
405
+ aria-hidden="true"
406
+ class="h-4 w-4"
407
+ viewBox="0 0 24 24"
408
+ fill="none"
409
+ stroke="currentColor"
410
+ stroke-width="2"
411
+ stroke-linecap="round"
412
+ stroke-linejoin="round"
413
+ >
414
+ <line x1="10" y1="6" x2="21" y2="6" />
415
+ <line x1="10" y1="12" x2="21" y2="12" />
416
+ <line x1="10" y1="18" x2="21" y2="18" />
417
+ <path d="M4 6h1v4" />
418
+ <path d="M4 10h2" />
419
+ <path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1" />
420
+ </svg>
421
+ </button>
422
+
423
+ <div class="mx-1 h-6 w-px bg-border" role="separator"></div>
424
+
425
+ <button
426
+ type="button"
427
+ class="inline-flex h-8 w-8 items-center justify-center rounded text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground"
428
+ [class.bg-accent]="isBlockquote()"
429
+ [class.text-accent-foreground]="isBlockquote()"
430
+ title="Blockquote"
431
+ aria-label="Blockquote"
432
+ [attr.aria-pressed]="isBlockquote()"
433
+ (click)="toggleBlockquote()"
434
+ >
435
+ <svg aria-hidden="true" class="h-4 w-4" viewBox="0 0 24 24" fill="currentColor">
436
+ <path
437
+ d="M4.583 17.321C3.553 16.227 3 15 3 13.011c0-3.5 2.457-6.637 6.03-8.188l.893 1.378c-3.335 1.804-3.987 4.145-4.247 5.621.537-.278 1.24-.375 1.929-.311 1.804.167 3.226 1.648 3.226 3.489a3.5 3.5 0 01-3.5 3.5c-1.073 0-2.099-.49-2.748-1.179zm10 0C13.553 16.227 13 15 13 13.011c0-3.5 2.457-6.637 6.03-8.188l.893 1.378c-3.335 1.804-3.987 4.145-4.247 5.621.537-.278 1.24-.375 1.929-.311 1.804.167 3.226 1.648 3.226 3.489a3.5 3.5 0 01-3.5 3.5c-1.073 0-2.099-.49-2.748-1.179z"
438
+ />
439
+ </svg>
440
+ </button>
441
+
442
+ <button
443
+ type="button"
444
+ class="inline-flex h-8 w-8 items-center justify-center rounded text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground"
445
+ [class.bg-accent]="isCodeBlock()"
446
+ [class.text-accent-foreground]="isCodeBlock()"
447
+ title="Code Block"
448
+ aria-label="Code block"
449
+ [attr.aria-pressed]="isCodeBlock()"
450
+ (click)="toggleCodeBlock()"
451
+ >
452
+ <svg
453
+ aria-hidden="true"
454
+ class="h-4 w-4"
455
+ viewBox="0 0 24 24"
456
+ fill="none"
457
+ stroke="currentColor"
458
+ stroke-width="2"
459
+ stroke-linecap="round"
460
+ stroke-linejoin="round"
461
+ >
462
+ <polyline points="16 18 22 12 16 6" />
463
+ <polyline points="8 6 2 12 8 18" />
464
+ </svg>
465
+ </button>
466
+
467
+ <button
468
+ type="button"
469
+ class="inline-flex h-8 w-8 items-center justify-center rounded text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground"
470
+ title="Horizontal Rule"
471
+ aria-label="Horizontal rule"
472
+ (click)="insertHorizontalRule()"
473
+ >
474
+ <svg
475
+ aria-hidden="true"
476
+ class="h-4 w-4"
477
+ viewBox="0 0 24 24"
478
+ fill="none"
479
+ stroke="currentColor"
480
+ stroke-width="2"
481
+ stroke-linecap="round"
482
+ >
483
+ <line x1="3" y1="12" x2="21" y2="12" />
484
+ </svg>
485
+ </button>
486
+ </div>
487
+
488
+ <!-- Editor content area -->
489
+ <div
490
+ #editorElement
491
+ class="tiptap-editor prose prose-sm dark:prose-invert max-w-none px-3 py-2"
492
+ data-testid="rich-text-editor"
493
+ ></div>
494
+ </div>
495
+ } @else {
496
+ <!-- Read-only view -->
497
+ <div
498
+ class="prose prose-sm dark:prose-invert max-w-none rounded-md border border-input bg-muted/50 px-3 py-2"
499
+ [innerHTML]="stringValue()"
500
+ ></div>
501
+ }
502
+ </mcms-form-field>
503
+ `, isInline: true, styles: [":host ::ng-deep .tiptap-editor{min-height:200px;outline:none}:host ::ng-deep .tiptap-editor .ProseMirror{outline:none;min-height:200px}:host ::ng-deep .tiptap-editor .ProseMirror p.is-editor-empty:first-child:before{content:attr(data-placeholder);float:left;color:hsl(var(--mcms-muted-foreground));pointer-events:none;height:0}:host ::ng-deep .tiptap-editor .ProseMirror h1{font-size:1.5em;font-weight:700;margin:.5em 0}:host ::ng-deep .tiptap-editor .ProseMirror h2{font-size:1.25em;font-weight:600;margin:.5em 0}:host ::ng-deep .tiptap-editor .ProseMirror h3{font-size:1.1em;font-weight:600;margin:.5em 0}:host ::ng-deep .tiptap-editor .ProseMirror ul{list-style:disc;padding-left:1.5em}:host ::ng-deep .tiptap-editor .ProseMirror ol{list-style:decimal;padding-left:1.5em}:host ::ng-deep .tiptap-editor .ProseMirror blockquote{border-left:3px solid hsl(var(--mcms-border));padding-left:1em;margin-left:0;color:hsl(var(--mcms-muted-foreground))}:host ::ng-deep .tiptap-editor .ProseMirror pre{background:hsl(var(--mcms-muted));border-radius:.375rem;padding:.75em;font-family:monospace;font-size:.875em}:host ::ng-deep .tiptap-editor .ProseMirror code{background:hsl(var(--mcms-muted));border-radius:.25rem;padding:.125em .25em;font-size:.875em}:host ::ng-deep .tiptap-editor .ProseMirror hr{border:none;border-top:1px solid hsl(var(--mcms-border));margin:1em 0}:host ::ng-deep .tiptap-editor .ProseMirror a{color:hsl(var(--mcms-primary));text-decoration:underline}:host ::ng-deep .tiptap-editor .ProseMirror p{margin:.25em 0}\n"], dependencies: [{ kind: "component", type: McmsFormField, selector: "mcms-form-field", inputs: ["id", "required", "disabled", "errors", "hint", "hasLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
504
+ }
505
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: RichTextFieldRenderer, decorators: [{
506
+ type: Component,
507
+ args: [{ selector: 'mcms-rich-text-field-renderer', imports: [McmsFormField], changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'block' }, template: `
508
+ <mcms-form-field
509
+ [id]="fieldId()"
510
+ [required]="required()"
511
+ [disabled]="isDisabled()"
512
+ [errors]="touchedErrors()"
513
+ >
514
+ <span mcmsLabel>{{ label() }}</span>
515
+
516
+ @if (!isDisabled()) {
517
+ <div
518
+ class="rounded-md border border-input bg-background focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2"
519
+ >
520
+ <!-- Toolbar -->
521
+ <div
522
+ class="flex flex-wrap items-center gap-0.5 border-b border-input px-1 py-1"
523
+ role="toolbar"
524
+ [attr.aria-label]="label() + ' formatting'"
525
+ >
526
+ <button
527
+ type="button"
528
+ class="inline-flex h-8 w-8 items-center justify-center rounded text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground"
529
+ [class.bg-accent]="isBold()"
530
+ [class.text-accent-foreground]="isBold()"
531
+ title="Bold (Ctrl+B)"
532
+ aria-label="Bold"
533
+ [attr.aria-pressed]="isBold()"
534
+ (click)="toggleBold()"
535
+ >
536
+ <svg
537
+ aria-hidden="true"
538
+ class="h-4 w-4"
539
+ viewBox="0 0 24 24"
540
+ fill="none"
541
+ stroke="currentColor"
542
+ stroke-width="3"
543
+ stroke-linecap="round"
544
+ stroke-linejoin="round"
545
+ >
546
+ <path d="M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z" />
547
+ <path d="M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z" />
548
+ </svg>
549
+ </button>
550
+
551
+ <button
552
+ type="button"
553
+ class="inline-flex h-8 w-8 items-center justify-center rounded text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground"
554
+ [class.bg-accent]="isItalic()"
555
+ [class.text-accent-foreground]="isItalic()"
556
+ title="Italic (Ctrl+I)"
557
+ aria-label="Italic"
558
+ [attr.aria-pressed]="isItalic()"
559
+ (click)="toggleItalic()"
560
+ >
561
+ <svg
562
+ aria-hidden="true"
563
+ class="h-4 w-4"
564
+ viewBox="0 0 24 24"
565
+ fill="none"
566
+ stroke="currentColor"
567
+ stroke-width="2"
568
+ stroke-linecap="round"
569
+ stroke-linejoin="round"
570
+ >
571
+ <line x1="19" y1="4" x2="10" y2="4" />
572
+ <line x1="14" y1="20" x2="5" y2="20" />
573
+ <line x1="15" y1="4" x2="9" y2="20" />
574
+ </svg>
575
+ </button>
576
+
577
+ <button
578
+ type="button"
579
+ class="inline-flex h-8 w-8 items-center justify-center rounded text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground"
580
+ [class.bg-accent]="isUnderline()"
581
+ [class.text-accent-foreground]="isUnderline()"
582
+ title="Underline (Ctrl+U)"
583
+ aria-label="Underline"
584
+ [attr.aria-pressed]="isUnderline()"
585
+ (click)="toggleUnderline()"
586
+ >
587
+ <svg
588
+ aria-hidden="true"
589
+ class="h-4 w-4"
590
+ viewBox="0 0 24 24"
591
+ fill="none"
592
+ stroke="currentColor"
593
+ stroke-width="2"
594
+ stroke-linecap="round"
595
+ stroke-linejoin="round"
596
+ >
597
+ <path d="M6 3v7a6 6 0 0 0 6 6 6 6 0 0 0 6-6V3" />
598
+ <line x1="4" y1="21" x2="20" y2="21" />
599
+ </svg>
600
+ </button>
601
+
602
+ <button
603
+ type="button"
604
+ class="inline-flex h-8 w-8 items-center justify-center rounded text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground"
605
+ [class.bg-accent]="isStrike()"
606
+ [class.text-accent-foreground]="isStrike()"
607
+ title="Strikethrough"
608
+ aria-label="Strikethrough"
609
+ [attr.aria-pressed]="isStrike()"
610
+ (click)="toggleStrike()"
611
+ >
612
+ <svg
613
+ aria-hidden="true"
614
+ class="h-4 w-4"
615
+ viewBox="0 0 24 24"
616
+ fill="none"
617
+ stroke="currentColor"
618
+ stroke-width="2"
619
+ stroke-linecap="round"
620
+ stroke-linejoin="round"
621
+ >
622
+ <path d="M16 4H9a3 3 0 0 0-2.83 4" />
623
+ <path d="M14 12a4 4 0 0 1 0 8H6" />
624
+ <line x1="4" y1="12" x2="20" y2="12" />
625
+ </svg>
626
+ </button>
627
+
628
+ <div class="mx-1 h-6 w-px bg-border" role="separator"></div>
629
+
630
+ <button
631
+ type="button"
632
+ class="inline-flex h-8 w-8 items-center justify-center rounded text-sm font-bold transition-colors hover:bg-accent hover:text-accent-foreground"
633
+ [class.bg-accent]="isHeading1()"
634
+ [class.text-accent-foreground]="isHeading1()"
635
+ title="Heading 1"
636
+ aria-label="Heading 1"
637
+ [attr.aria-pressed]="isHeading1()"
638
+ (click)="toggleHeading(1)"
639
+ >
640
+ H1
641
+ </button>
642
+
643
+ <button
644
+ type="button"
645
+ class="inline-flex h-8 w-8 items-center justify-center rounded text-sm font-bold transition-colors hover:bg-accent hover:text-accent-foreground"
646
+ [class.bg-accent]="isHeading2()"
647
+ [class.text-accent-foreground]="isHeading2()"
648
+ title="Heading 2"
649
+ aria-label="Heading 2"
650
+ [attr.aria-pressed]="isHeading2()"
651
+ (click)="toggleHeading(2)"
652
+ >
653
+ H2
654
+ </button>
655
+
656
+ <button
657
+ type="button"
658
+ class="inline-flex h-8 w-8 items-center justify-center rounded text-sm font-bold transition-colors hover:bg-accent hover:text-accent-foreground"
659
+ [class.bg-accent]="isHeading3()"
660
+ [class.text-accent-foreground]="isHeading3()"
661
+ title="Heading 3"
662
+ aria-label="Heading 3"
663
+ [attr.aria-pressed]="isHeading3()"
664
+ (click)="toggleHeading(3)"
665
+ >
666
+ H3
667
+ </button>
668
+
669
+ <div class="mx-1 h-6 w-px bg-border" role="separator"></div>
670
+
671
+ <button
672
+ type="button"
673
+ class="inline-flex h-8 w-8 items-center justify-center rounded text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground"
674
+ [class.bg-accent]="isBulletList()"
675
+ [class.text-accent-foreground]="isBulletList()"
676
+ title="Bullet List"
677
+ aria-label="Bullet list"
678
+ [attr.aria-pressed]="isBulletList()"
679
+ (click)="toggleBulletList()"
680
+ >
681
+ <svg
682
+ aria-hidden="true"
683
+ class="h-4 w-4"
684
+ viewBox="0 0 24 24"
685
+ fill="none"
686
+ stroke="currentColor"
687
+ stroke-width="2"
688
+ stroke-linecap="round"
689
+ stroke-linejoin="round"
690
+ >
691
+ <line x1="8" y1="6" x2="21" y2="6" />
692
+ <line x1="8" y1="12" x2="21" y2="12" />
693
+ <line x1="8" y1="18" x2="21" y2="18" />
694
+ <line x1="3" y1="6" x2="3.01" y2="6" />
695
+ <line x1="3" y1="12" x2="3.01" y2="12" />
696
+ <line x1="3" y1="18" x2="3.01" y2="18" />
697
+ </svg>
698
+ </button>
699
+
700
+ <button
701
+ type="button"
702
+ class="inline-flex h-8 w-8 items-center justify-center rounded text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground"
703
+ [class.bg-accent]="isOrderedList()"
704
+ [class.text-accent-foreground]="isOrderedList()"
705
+ title="Numbered List"
706
+ aria-label="Numbered list"
707
+ [attr.aria-pressed]="isOrderedList()"
708
+ (click)="toggleOrderedList()"
709
+ >
710
+ <svg
711
+ aria-hidden="true"
712
+ class="h-4 w-4"
713
+ viewBox="0 0 24 24"
714
+ fill="none"
715
+ stroke="currentColor"
716
+ stroke-width="2"
717
+ stroke-linecap="round"
718
+ stroke-linejoin="round"
719
+ >
720
+ <line x1="10" y1="6" x2="21" y2="6" />
721
+ <line x1="10" y1="12" x2="21" y2="12" />
722
+ <line x1="10" y1="18" x2="21" y2="18" />
723
+ <path d="M4 6h1v4" />
724
+ <path d="M4 10h2" />
725
+ <path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1" />
726
+ </svg>
727
+ </button>
728
+
729
+ <div class="mx-1 h-6 w-px bg-border" role="separator"></div>
730
+
731
+ <button
732
+ type="button"
733
+ class="inline-flex h-8 w-8 items-center justify-center rounded text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground"
734
+ [class.bg-accent]="isBlockquote()"
735
+ [class.text-accent-foreground]="isBlockquote()"
736
+ title="Blockquote"
737
+ aria-label="Blockquote"
738
+ [attr.aria-pressed]="isBlockquote()"
739
+ (click)="toggleBlockquote()"
740
+ >
741
+ <svg aria-hidden="true" class="h-4 w-4" viewBox="0 0 24 24" fill="currentColor">
742
+ <path
743
+ d="M4.583 17.321C3.553 16.227 3 15 3 13.011c0-3.5 2.457-6.637 6.03-8.188l.893 1.378c-3.335 1.804-3.987 4.145-4.247 5.621.537-.278 1.24-.375 1.929-.311 1.804.167 3.226 1.648 3.226 3.489a3.5 3.5 0 01-3.5 3.5c-1.073 0-2.099-.49-2.748-1.179zm10 0C13.553 16.227 13 15 13 13.011c0-3.5 2.457-6.637 6.03-8.188l.893 1.378c-3.335 1.804-3.987 4.145-4.247 5.621.537-.278 1.24-.375 1.929-.311 1.804.167 3.226 1.648 3.226 3.489a3.5 3.5 0 01-3.5 3.5c-1.073 0-2.099-.49-2.748-1.179z"
744
+ />
745
+ </svg>
746
+ </button>
747
+
748
+ <button
749
+ type="button"
750
+ class="inline-flex h-8 w-8 items-center justify-center rounded text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground"
751
+ [class.bg-accent]="isCodeBlock()"
752
+ [class.text-accent-foreground]="isCodeBlock()"
753
+ title="Code Block"
754
+ aria-label="Code block"
755
+ [attr.aria-pressed]="isCodeBlock()"
756
+ (click)="toggleCodeBlock()"
757
+ >
758
+ <svg
759
+ aria-hidden="true"
760
+ class="h-4 w-4"
761
+ viewBox="0 0 24 24"
762
+ fill="none"
763
+ stroke="currentColor"
764
+ stroke-width="2"
765
+ stroke-linecap="round"
766
+ stroke-linejoin="round"
767
+ >
768
+ <polyline points="16 18 22 12 16 6" />
769
+ <polyline points="8 6 2 12 8 18" />
770
+ </svg>
771
+ </button>
772
+
773
+ <button
774
+ type="button"
775
+ class="inline-flex h-8 w-8 items-center justify-center rounded text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground"
776
+ title="Horizontal Rule"
777
+ aria-label="Horizontal rule"
778
+ (click)="insertHorizontalRule()"
779
+ >
780
+ <svg
781
+ aria-hidden="true"
782
+ class="h-4 w-4"
783
+ viewBox="0 0 24 24"
784
+ fill="none"
785
+ stroke="currentColor"
786
+ stroke-width="2"
787
+ stroke-linecap="round"
788
+ >
789
+ <line x1="3" y1="12" x2="21" y2="12" />
790
+ </svg>
791
+ </button>
792
+ </div>
793
+
794
+ <!-- Editor content area -->
795
+ <div
796
+ #editorElement
797
+ class="tiptap-editor prose prose-sm dark:prose-invert max-w-none px-3 py-2"
798
+ data-testid="rich-text-editor"
799
+ ></div>
800
+ </div>
801
+ } @else {
802
+ <!-- Read-only view -->
803
+ <div
804
+ class="prose prose-sm dark:prose-invert max-w-none rounded-md border border-input bg-muted/50 px-3 py-2"
805
+ [innerHTML]="stringValue()"
806
+ ></div>
807
+ }
808
+ </mcms-form-field>
809
+ `, styles: [":host ::ng-deep .tiptap-editor{min-height:200px;outline:none}:host ::ng-deep .tiptap-editor .ProseMirror{outline:none;min-height:200px}:host ::ng-deep .tiptap-editor .ProseMirror p.is-editor-empty:first-child:before{content:attr(data-placeholder);float:left;color:hsl(var(--mcms-muted-foreground));pointer-events:none;height:0}:host ::ng-deep .tiptap-editor .ProseMirror h1{font-size:1.5em;font-weight:700;margin:.5em 0}:host ::ng-deep .tiptap-editor .ProseMirror h2{font-size:1.25em;font-weight:600;margin:.5em 0}:host ::ng-deep .tiptap-editor .ProseMirror h3{font-size:1.1em;font-weight:600;margin:.5em 0}:host ::ng-deep .tiptap-editor .ProseMirror ul{list-style:disc;padding-left:1.5em}:host ::ng-deep .tiptap-editor .ProseMirror ol{list-style:decimal;padding-left:1.5em}:host ::ng-deep .tiptap-editor .ProseMirror blockquote{border-left:3px solid hsl(var(--mcms-border));padding-left:1em;margin-left:0;color:hsl(var(--mcms-muted-foreground))}:host ::ng-deep .tiptap-editor .ProseMirror pre{background:hsl(var(--mcms-muted));border-radius:.375rem;padding:.75em;font-family:monospace;font-size:.875em}:host ::ng-deep .tiptap-editor .ProseMirror code{background:hsl(var(--mcms-muted));border-radius:.25rem;padding:.125em .25em;font-size:.875em}:host ::ng-deep .tiptap-editor .ProseMirror hr{border:none;border-top:1px solid hsl(var(--mcms-border));margin:1em 0}:host ::ng-deep .tiptap-editor .ProseMirror a{color:hsl(var(--mcms-primary));text-decoration:underline}:host ::ng-deep .tiptap-editor .ProseMirror p{margin:.25em 0}\n"] }]
810
+ }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], formNode: [{ type: i0.Input, args: [{ isSignal: true, alias: "formNode", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], path: [{ type: i0.Input, args: [{ isSignal: true, alias: "path", required: true }] }], editorRef: [{ type: i0.ViewChild, args: ['editorElement', { isSignal: true }] }] } });
811
+
812
+ export { RichTextFieldRenderer };
813
+ //# sourceMappingURL=momentumcms-admin-rich-text-field.component-DKQ6pwp7.mjs.map