@nuasite/cms 0.31.0 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/editor.js +4216 -4205
- package/package.json +1 -1
- package/src/editor/constants.ts +3 -0
- package/src/editor/dom.ts +3 -0
- package/src/editor/editor.ts +27 -0
- package/src/field-types.ts +15 -1
package/package.json
CHANGED
package/src/editor/constants.ts
CHANGED
|
@@ -109,6 +109,9 @@ export const CSS = {
|
|
|
109
109
|
HIGHLIGHT_ELEMENT: 'cms-highlight-overlay',
|
|
110
110
|
/** Data attribute for background image elements */
|
|
111
111
|
BG_IMAGE_ATTRIBUTE: 'data-cms-bg-img',
|
|
112
|
+
/** Data attribute set during edit mode on elements whose manifest entry has no source path;
|
|
113
|
+
* suppresses hover outlines and blocks interaction so the user doesn't type into a dead-end. */
|
|
114
|
+
LOCKED_ATTRIBUTE: 'data-cms-locked',
|
|
112
115
|
} as const
|
|
113
116
|
|
|
114
117
|
/**
|
package/src/editor/dom.ts
CHANGED
|
@@ -76,6 +76,8 @@ export function getCmsElementAtPosition(
|
|
|
76
76
|
if (!el.hasAttribute(CSS.ID_ATTRIBUTE)) continue
|
|
77
77
|
// Skip component roots - they should be handled separately
|
|
78
78
|
if (el.hasAttribute(CSS.COMPONENT_ID_ATTRIBUTE)) continue
|
|
79
|
+
// Skip elements locked because their manifest entry has no source path
|
|
80
|
+
if (el.hasAttribute(CSS.LOCKED_ATTRIBUTE)) continue
|
|
79
81
|
|
|
80
82
|
const cmsId = el.getAttribute(CSS.ID_ATTRIBUTE)
|
|
81
83
|
|
|
@@ -96,6 +98,7 @@ export function getCmsElementAtPosition(
|
|
|
96
98
|
if (!(el instanceof HTMLElement)) continue
|
|
97
99
|
if (!el.hasAttribute(CSS.ID_ATTRIBUTE)) continue
|
|
98
100
|
if (el.hasAttribute(CSS.COMPONENT_ID_ATTRIBUTE)) continue
|
|
101
|
+
if (el.hasAttribute(CSS.LOCKED_ATTRIBUTE)) continue
|
|
99
102
|
|
|
100
103
|
const cmsId = el.getAttribute(CSS.ID_ATTRIBUTE)
|
|
101
104
|
|
package/src/editor/editor.ts
CHANGED
|
@@ -76,6 +76,7 @@ const INLINE_STYLE_ELEMENTS = [
|
|
|
76
76
|
// merge a burst, short enough that the next deliberate action still explains itself.
|
|
77
77
|
const FORMATTING_BLOCKED_TOAST_COOLDOWN_MS = 3000
|
|
78
78
|
let lastFormattingBlockedToastAt = 0
|
|
79
|
+
let lastLockedToastAt = 0
|
|
79
80
|
|
|
80
81
|
// Signals listener cleanup on stopEditMode. Aborting removes every listener
|
|
81
82
|
// attached with { signal } in the current edit session in one shot.
|
|
@@ -90,6 +91,21 @@ function notifyFormattingBlocked(): void {
|
|
|
90
91
|
signals.showToast("Formatting isn't available — this text is used as a plain value", 'info')
|
|
91
92
|
}
|
|
92
93
|
|
|
94
|
+
export function notifyLockedElement(): void {
|
|
95
|
+
const now = Date.now()
|
|
96
|
+
if (now - lastLockedToastAt < FORMATTING_BLOCKED_TOAST_COOLDOWN_MS) {
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
lastLockedToastAt = now
|
|
100
|
+
signals.showToast("This text can't be edited here — no source file is linked to it", 'info')
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Test-only: reset toast throttle state between test cases. */
|
|
104
|
+
export function _resetToastThrottles(): void {
|
|
105
|
+
lastFormattingBlockedToastAt = 0
|
|
106
|
+
lastLockedToastAt = 0
|
|
107
|
+
}
|
|
108
|
+
|
|
93
109
|
// Uses the Selection/Range API rather than the deprecated document.execCommand('insertText').
|
|
94
110
|
export function insertPlainTextAtRange(range: Range, text: string): boolean {
|
|
95
111
|
if (!text) return false
|
|
@@ -224,6 +240,16 @@ export async function startEditMode(
|
|
|
224
240
|
return
|
|
225
241
|
}
|
|
226
242
|
|
|
243
|
+
// Without a source path, the writer has nowhere to persist text edits — lock
|
|
244
|
+
// the element so it can't be typed into and the user gets told why on click.
|
|
245
|
+
if (!manifestEntry?.sourcePath) {
|
|
246
|
+
logDebug(config.debug, 'Skipping element without source path:', cmsId)
|
|
247
|
+
makeElementNonEditable(el)
|
|
248
|
+
el.setAttribute(CSS.LOCKED_ATTRIBUTE, 'true')
|
|
249
|
+
el.addEventListener('click', notifyLockedElement, { signal: editModeSignal })
|
|
250
|
+
return
|
|
251
|
+
}
|
|
252
|
+
|
|
227
253
|
makeElementEditable(el)
|
|
228
254
|
|
|
229
255
|
const stylingAllowed = manifestEntry?.allowStyling !== false
|
|
@@ -434,6 +460,7 @@ export function stopEditMode(onStateChange?: () => void): void {
|
|
|
434
460
|
|
|
435
461
|
getAllCmsElements().forEach(el => {
|
|
436
462
|
makeElementNonEditable(el)
|
|
463
|
+
el.removeAttribute(CSS.LOCKED_ATTRIBUTE)
|
|
437
464
|
})
|
|
438
465
|
}
|
|
439
466
|
|
package/src/field-types.ts
CHANGED
|
@@ -65,10 +65,24 @@ const toISODate = (v: unknown) => (v instanceof Date ? v.toISOString().slice(0,
|
|
|
65
65
|
/** Normalize YAML Date objects to ISO datetime strings */
|
|
66
66
|
const toISODatetime = (v: unknown) => (v instanceof Date ? v.toISOString() : v)
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
const WRAPPING_METHODS = ['default', 'optional', 'nullable', 'nullish'] as const
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Add a chainable `.orderBy()` method to a Zod schema. The scanner detects it from source code.
|
|
72
|
+
*
|
|
73
|
+
* Also wraps the Zod methods that produce a new schema instance (`.default()`, `.optional()`,
|
|
74
|
+
* `.nullable()`, `.nullish()`) so the marker survives those wrappers — chain order doesn't matter.
|
|
75
|
+
*/
|
|
69
76
|
function withOrderBy<T extends z.ZodTypeAny>(schema: T): WithOrderBy<T> {
|
|
70
77
|
const s = schema as WithOrderBy<T>
|
|
71
78
|
s.orderBy = (_direction?: OrderByDirection) => schema
|
|
79
|
+
for (const method of WRAPPING_METHODS) {
|
|
80
|
+
const original = (schema as unknown as Record<string, unknown>)[method]
|
|
81
|
+
if (typeof original !== 'function') continue
|
|
82
|
+
;(s as unknown as Record<string, unknown>)[method] = function(this: unknown, ...args: unknown[]) {
|
|
83
|
+
return withOrderBy((original as (...a: unknown[]) => z.ZodTypeAny).apply(this, args))
|
|
84
|
+
}
|
|
85
|
+
}
|
|
72
86
|
return s
|
|
73
87
|
}
|
|
74
88
|
|