@panphora/hyper-cms 0.11.6 → 0.13.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/README.md +91 -33
- package/dist/hypercms.min.js +158 -126
- package/package.json +5 -3
- package/src/apply-loop.js +3 -2
- package/src/catalogs/colors.js +293 -0
- package/src/catalogs/fonts.js +126 -0
- package/src/catalogs/index.js +3 -0
- package/src/catalogs/variant-icons.js +24 -0
- package/src/color-control.js +196 -0
- package/src/events.js +25 -2
- package/src/font-control.js +142 -0
- package/src/fonts-link.js +83 -0
- package/src/form-builder.js +85 -3
- package/src/form-rules.js +48 -3
- package/src/hypercms.js +111 -31
- package/src/page-root.js +28 -0
- package/src/refresh.js +4 -1
- package/src/session.js +51 -3
- package/src/targets.js +55 -13
- package/src/templates.js +100 -6
- package/src/theme/pixel-quiet.overrides.css +93 -8
- package/src/theme.generated.css +93 -8
- package/src/variant-control.js +46 -0
- package/src/views/inline-layer.js +73 -18
- package/src/views/inline.js +87 -17
- package/src/toggle.js +0 -681
|
@@ -40,7 +40,7 @@ function hosts(row, strip) {
|
|
|
40
40
|
return strip.width <= row.width && strip.height <= row.height
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
export function createInlineLayer({ doc, layerEl, onActivate, onListAction }) {
|
|
43
|
+
export function createInlineLayer({ doc, layerEl, onActivate, onListAction, onStyle }) {
|
|
44
44
|
const win = doc.defaultView
|
|
45
45
|
// Every positioned thing the layer draws, whatever kind: a handle, a row
|
|
46
46
|
// strip, a list's Add. One array, because all three are placed by one pass.
|
|
@@ -93,9 +93,8 @@ export function createInlineLayer({ doc, layerEl, onActivate, onListAction }) {
|
|
|
93
93
|
|
|
94
94
|
function schedule() {
|
|
95
95
|
if (frame || !win) return
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
// mount. Every browser has it; a non-visual jsdom does not.
|
|
96
|
+
// A window with no requestAnimationFrame gets a synchronous pass instead of
|
|
97
|
+
// a crash on mount. Every browser has it; a non-visual jsdom does not.
|
|
99
98
|
if (typeof win.requestAnimationFrame !== 'function') return placeAll()
|
|
100
99
|
frame = win.requestAnimationFrame(() => {
|
|
101
100
|
frame = 0
|
|
@@ -135,6 +134,14 @@ export function createInlineLayer({ doc, layerEl, onActivate, onListAction }) {
|
|
|
135
134
|
spot.node.hidden = true
|
|
136
135
|
continue
|
|
137
136
|
}
|
|
137
|
+
// A Style chip sits inside its element's top-left corner, clear of the
|
|
138
|
+
// row strips and handles that take the top-right.
|
|
139
|
+
if (spot.kind === 'style') {
|
|
140
|
+
const x = Math.max(8, Math.min(anchor.left, viewport.width - handle.width - 8))
|
|
141
|
+
const y = Math.max(8, Math.min(anchor.top, viewport.height - handle.height - 8))
|
|
142
|
+
spot.node.style.transform = `translate(${Math.round(x)}px, ${Math.round(y)}px)`
|
|
143
|
+
continue
|
|
144
|
+
}
|
|
138
145
|
const prefer = spot.kind === 'handle' ? null : CONTROL_PLACEMENT
|
|
139
146
|
let { x, y } = placeHandle({ anchor, handle, viewport, ...prefer })
|
|
140
147
|
const pencil = spot.members.find((member) => member.kind === 'handle')
|
|
@@ -272,6 +279,7 @@ export function createInlineLayer({ doc, layerEl, onActivate, onListAction }) {
|
|
|
272
279
|
|
|
273
280
|
function sameMember(member, spec) {
|
|
274
281
|
if (member.kind !== spec.kind) return false
|
|
282
|
+
if (member.kind === 'style') return true
|
|
275
283
|
if (member.kind === 'handle') return targetIdentity(member.target) === targetIdentity(spec.target)
|
|
276
284
|
if (member.kind === 'row') return member.row === spec.row && sameList(member.list, spec.list)
|
|
277
285
|
return sameList(member.list, spec.list)
|
|
@@ -301,6 +309,21 @@ export function createInlineLayer({ doc, layerEl, onActivate, onListAction }) {
|
|
|
301
309
|
return member
|
|
302
310
|
}
|
|
303
311
|
|
|
312
|
+
function makeStyleChip(spec) {
|
|
313
|
+
const button = doc.createElement('button')
|
|
314
|
+
button.type = 'button'
|
|
315
|
+
button.className = 'hcms-inline-style-chip mirk-button mirk-button--small'
|
|
316
|
+
button.setAttribute('aria-label', 'Edit style')
|
|
317
|
+
button.innerHTML = '<span class="mirk-button__label">Style</span>'
|
|
318
|
+
const member = { node: button, kind: 'style', group: spec.group }
|
|
319
|
+
button.addEventListener('click', (event) => {
|
|
320
|
+
event.preventDefault()
|
|
321
|
+
event.stopPropagation()
|
|
322
|
+
onStyle?.(member.group, button)
|
|
323
|
+
})
|
|
324
|
+
return member
|
|
325
|
+
}
|
|
326
|
+
|
|
304
327
|
function makeListButton(action, label) {
|
|
305
328
|
const button = doc.createElement('button')
|
|
306
329
|
button.type = 'button'
|
|
@@ -380,15 +403,23 @@ export function createInlineLayer({ doc, layerEl, onActivate, onListAction }) {
|
|
|
380
403
|
} else if (event.key === 'Tab') closeSettings(true)
|
|
381
404
|
else if (['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key) && openSettings.menu.contains(event.target)) {
|
|
382
405
|
event.preventDefault()
|
|
383
|
-
openSettings.menu.
|
|
406
|
+
const items = [...openSettings.menu.querySelectorAll('[role="menuitem"]')]
|
|
407
|
+
const at = items.indexOf(event.target)
|
|
408
|
+
const next = event.key === 'Home' ? 0
|
|
409
|
+
: event.key === 'End' ? items.length - 1
|
|
410
|
+
: at === -1 ? 0
|
|
411
|
+
: (at + (event.key === 'ArrowDown' ? 1 : -1) + items.length) % items.length
|
|
412
|
+
items[next]?.focus({ preventScroll: true })
|
|
384
413
|
}
|
|
385
414
|
}
|
|
386
415
|
|
|
387
|
-
function updateSettings(member, { list, row, rowIndex }) {
|
|
388
|
-
Object.assign(member, { list, row, index: rowIndex })
|
|
416
|
+
function updateSettings(member, { list, row, rowIndex, style = null }) {
|
|
417
|
+
Object.assign(member, { list, row, index: rowIndex, style })
|
|
389
418
|
member.node.setAttribute('data-hcms-list', list.path.join('.'))
|
|
390
419
|
member.node.setAttribute('data-hcms-row', String(rowIndex))
|
|
391
420
|
member.button.setAttribute('aria-label', `Settings ${list.path.join('.')}.${rowIndex}`)
|
|
421
|
+
if (style && !member.styleItem.isConnected) member.menu.insertBefore(member.styleItem, member.menu.firstChild)
|
|
422
|
+
else if (!style) member.styleItem.remove()
|
|
392
423
|
}
|
|
393
424
|
|
|
394
425
|
function makeSettings(spec) {
|
|
@@ -402,6 +433,11 @@ export function createInlineLayer({ doc, layerEl, onActivate, onListAction }) {
|
|
|
402
433
|
menu.setAttribute('role', 'menu')
|
|
403
434
|
menu.setAttribute('aria-label', 'Item settings')
|
|
404
435
|
menu.hidden = true
|
|
436
|
+
const styleItem = doc.createElement('button')
|
|
437
|
+
styleItem.type = 'button'
|
|
438
|
+
styleItem.setAttribute('role', 'menuitem')
|
|
439
|
+
styleItem.setAttribute('data-hcms-style-action', '')
|
|
440
|
+
styleItem.textContent = 'Style'
|
|
405
441
|
const remove = doc.createElement('button')
|
|
406
442
|
remove.type = 'button'
|
|
407
443
|
remove.setAttribute('role', 'menuitem')
|
|
@@ -409,7 +445,7 @@ export function createInlineLayer({ doc, layerEl, onActivate, onListAction }) {
|
|
|
409
445
|
remove.textContent = 'Delete'
|
|
410
446
|
menu.appendChild(remove)
|
|
411
447
|
node.append(button, menu)
|
|
412
|
-
const member = { node, button, menu, kind: 'settings' }
|
|
448
|
+
const member = { node, button, menu, styleItem, kind: 'settings' }
|
|
413
449
|
updateSettings(member, spec)
|
|
414
450
|
const show = () => {
|
|
415
451
|
closeSettings()
|
|
@@ -417,7 +453,7 @@ export function createInlineLayer({ doc, layerEl, onActivate, onListAction }) {
|
|
|
417
453
|
menu.hidden = false
|
|
418
454
|
button.setAttribute('aria-expanded', 'true')
|
|
419
455
|
node.parentElement.classList.add('has-open-settings')
|
|
420
|
-
|
|
456
|
+
menu.querySelector('[role="menuitem"]').focus({ preventScroll: true })
|
|
421
457
|
schedule()
|
|
422
458
|
}
|
|
423
459
|
button.addEventListener('click', (event) => {
|
|
@@ -438,6 +474,12 @@ export function createInlineLayer({ doc, layerEl, onActivate, onListAction }) {
|
|
|
438
474
|
closeSettings(true)
|
|
439
475
|
onListAction?.({ action: 'remove', list: member.list, index: member.index, row: member.row })
|
|
440
476
|
})
|
|
477
|
+
styleItem.addEventListener('click', (event) => {
|
|
478
|
+
event.preventDefault()
|
|
479
|
+
event.stopPropagation()
|
|
480
|
+
closeSettings()
|
|
481
|
+
onStyle?.(member.style, member.button)
|
|
482
|
+
})
|
|
441
483
|
return member
|
|
442
484
|
}
|
|
443
485
|
|
|
@@ -445,6 +487,7 @@ export function createInlineLayer({ doc, layerEl, onActivate, onListAction }) {
|
|
|
445
487
|
if (member.kind === 'handle') updateHandle(member, spec.target)
|
|
446
488
|
else if (member.kind === 'row') updateRowControls(member, spec)
|
|
447
489
|
else if (member.kind === 'settings') updateSettings(member, spec)
|
|
490
|
+
else if (member.kind === 'style') member.group = spec.group
|
|
448
491
|
}
|
|
449
492
|
|
|
450
493
|
function addDesired(desired, desiredIndex, el, spec, category) {
|
|
@@ -459,7 +502,7 @@ export function createInlineLayer({ doc, layerEl, onActivate, onListAction }) {
|
|
|
459
502
|
spot.members.push(spec)
|
|
460
503
|
}
|
|
461
504
|
|
|
462
|
-
function addListControls(desired, desiredIndex, list) {
|
|
505
|
+
function addListControls(desired, desiredIndex, list, rowStyles) {
|
|
463
506
|
const rows = list.items || []
|
|
464
507
|
rows.forEach((el, i) => {
|
|
465
508
|
if (!isAnchorable(el)) return
|
|
@@ -467,7 +510,7 @@ export function createInlineLayer({ doc, layerEl, onActivate, onListAction }) {
|
|
|
467
510
|
kind: 'row', list, row: el, rowIndex: i, count: rows.length,
|
|
468
511
|
}, 'item')
|
|
469
512
|
addDesired(desired, desiredIndex, el, {
|
|
470
|
-
kind: 'settings', list, row: el, rowIndex: i,
|
|
513
|
+
kind: 'settings', list, row: el, rowIndex: i, style: rowStyles.get(el) || null,
|
|
471
514
|
}, 'item')
|
|
472
515
|
})
|
|
473
516
|
}
|
|
@@ -481,7 +524,9 @@ export function createInlineLayer({ doc, layerEl, onActivate, onListAction }) {
|
|
|
481
524
|
? makeHandle(spec.target)
|
|
482
525
|
: spec.kind === 'row'
|
|
483
526
|
? makeRowControls(spec.list, spec.row, spec.rowIndex, spec.count)
|
|
484
|
-
:
|
|
527
|
+
: spec.kind === 'style'
|
|
528
|
+
? makeStyleChip(spec)
|
|
529
|
+
: makeSettings(spec))
|
|
485
530
|
unused.delete(member)
|
|
486
531
|
updateMember(member, spec)
|
|
487
532
|
if (controlsHidden && member.kind !== 'handle') member.node.hidden = true
|
|
@@ -498,18 +543,26 @@ export function createInlineLayer({ doc, layerEl, onActivate, onListAction }) {
|
|
|
498
543
|
spot.members = next
|
|
499
544
|
}
|
|
500
545
|
|
|
501
|
-
function reconcile(targetList, lists) {
|
|
546
|
+
function reconcile(targetList, lists, groups) {
|
|
502
547
|
const desired = []
|
|
503
548
|
const desiredIndex = new Map()
|
|
504
549
|
const nextTargets = new Map()
|
|
505
550
|
let nextHandleCount = 0
|
|
506
551
|
for (const target of targetList || []) {
|
|
552
|
+
if (target.style) continue
|
|
507
553
|
if (!nextTargets.has(target.el)) nextTargets.set(target.el, target)
|
|
508
554
|
if (target.kind !== 'handle' || !isAnchorable(target.el)) continue
|
|
509
555
|
addDesired(desired, desiredIndex, target.el, { kind: 'handle', target }, 'item')
|
|
510
556
|
nextHandleCount++
|
|
511
557
|
}
|
|
512
|
-
|
|
558
|
+
const rowStyles = new Map()
|
|
559
|
+
for (const group of groups || []) {
|
|
560
|
+
if (group.scope === 'row') rowStyles.set(group.el, group)
|
|
561
|
+
else if (group.scope === 'section' && isAnchorable(group.el)) {
|
|
562
|
+
addDesired(desired, desiredIndex, group.el, { kind: 'style', group }, 'style')
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
for (const list of lists || []) addListControls(desired, desiredIndex, list, rowStyles)
|
|
513
566
|
|
|
514
567
|
const unused = new Set(placements)
|
|
515
568
|
const visibility = new Map()
|
|
@@ -540,8 +593,10 @@ export function createInlineLayer({ doc, layerEl, onActivate, onListAction }) {
|
|
|
540
593
|
reconcileMembers(spot, wanted.members)
|
|
541
594
|
spot.kind = spot.members.some((member) => member.kind === 'row')
|
|
542
595
|
? 'row'
|
|
543
|
-
: spot.members.some((member) => member.kind === 'handle')
|
|
544
|
-
|
|
596
|
+
: spot.members.some((member) => member.kind === 'handle')
|
|
597
|
+
? 'handle'
|
|
598
|
+
: spot.members.some((member) => member.kind === 'style') ? 'style' : 'add'
|
|
599
|
+
spot.node.setAttribute('aria-label', spot.category === 'add' ? 'List controls' : spot.category === 'style' ? 'Style controls' : 'Item controls')
|
|
545
600
|
next.push(spot)
|
|
546
601
|
}
|
|
547
602
|
for (const spot of unused) spot.node.remove()
|
|
@@ -580,8 +635,8 @@ export function createInlineLayer({ doc, layerEl, onActivate, onListAction }) {
|
|
|
580
635
|
}
|
|
581
636
|
|
|
582
637
|
return {
|
|
583
|
-
setTargets(list, lists) {
|
|
584
|
-
reconcile(list, lists)
|
|
638
|
+
setTargets(list, lists, groups = []) {
|
|
639
|
+
reconcile(list, lists, groups)
|
|
585
640
|
ghosts.setLists(lists)
|
|
586
641
|
syncObservation()
|
|
587
642
|
listen()
|
package/src/views/inline.js
CHANGED
|
@@ -36,7 +36,7 @@ import { applyUnresolvedState } from '../unresolved.js'
|
|
|
36
36
|
import { refreshForm } from '../refresh.js'
|
|
37
37
|
import { clearSessionOpen, markSessionOpen, reensureStyles } from '../shell.js'
|
|
38
38
|
import { isAnchorable } from '../anchor.js'
|
|
39
|
-
import { resolveTargets } from '../targets.js'
|
|
39
|
+
import { resolveTargets, styleGroups } from '../targets.js'
|
|
40
40
|
import { createContentView } from '../lib/content-dom.js'
|
|
41
41
|
import { morph } from 'hyper-morph'
|
|
42
42
|
import { place } from '../place.js'
|
|
@@ -54,6 +54,7 @@ import {
|
|
|
54
54
|
import { installSnapshotHook } from '../hooks.js'
|
|
55
55
|
import { createInlineLayer } from './inline-layer.js'
|
|
56
56
|
import { inlineIcon } from '../inline-icons.js'
|
|
57
|
+
import { contentRoot } from '../page-root.js'
|
|
57
58
|
|
|
58
59
|
const HOST_TAG = 'hypercms-inline'
|
|
59
60
|
|
|
@@ -89,6 +90,7 @@ export function createInlineView({ doc, pageRoot, opts = {} }) {
|
|
|
89
90
|
// the first placement toward wherever the last field happened to sit.
|
|
90
91
|
let activeTarget = null
|
|
91
92
|
let activeHandle = null
|
|
93
|
+
let groupsNow = []
|
|
92
94
|
let popMode = null
|
|
93
95
|
|
|
94
96
|
// Build one binding: the editor, the marker, the snapshot hook, and the two
|
|
@@ -258,6 +260,7 @@ export function createInlineView({ doc, pageRoot, opts = {} }) {
|
|
|
258
260
|
// The handle and the page click are two doors to one room.
|
|
259
261
|
onActivate: (target, handle) => this.activate(target, handle),
|
|
260
262
|
onListAction: (op) => this.listAction(op),
|
|
263
|
+
onStyle: (group, handle) => this.openStyle(group, handle),
|
|
261
264
|
})
|
|
262
265
|
layer.setFollower(() => this.placePopover())
|
|
263
266
|
host.closeEl.addEventListener('click', () => this.deactivate())
|
|
@@ -266,6 +269,12 @@ export function createInlineView({ doc, pageRoot, opts = {} }) {
|
|
|
266
269
|
event.stopPropagation()
|
|
267
270
|
this.toggleControls()
|
|
268
271
|
})
|
|
272
|
+
host.pageStyleEl.addEventListener('click', (event) => {
|
|
273
|
+
event.preventDefault()
|
|
274
|
+
event.stopPropagation()
|
|
275
|
+
const group = groupsNow.find((candidate) => candidate.scope === 'page')
|
|
276
|
+
if (group) this.openStyle(group, host.pageStyleEl)
|
|
277
|
+
})
|
|
269
278
|
// There is no drawer. What a field with no visual representation gets is
|
|
270
279
|
// this one tap out to the view that edits everything.
|
|
271
280
|
// The switch carries the session's options across (hypercms.js open()), so
|
|
@@ -339,7 +348,7 @@ export function createInlineView({ doc, pageRoot, opts = {} }) {
|
|
|
339
348
|
// per target: the target set is rebuilt on every refresh, and per-element
|
|
340
349
|
// listeners would have to be torn down and reattached with it.
|
|
341
350
|
bindPage() {
|
|
342
|
-
const root = this.ctx.pageRoot
|
|
351
|
+
const root = contentRoot(this.ctx.pageRoot)
|
|
343
352
|
const hostRoot = host.root
|
|
344
353
|
|
|
345
354
|
const onPointerOver = (event) => {
|
|
@@ -405,7 +414,7 @@ export function createInlineView({ doc, pageRoot, opts = {} }) {
|
|
|
405
414
|
// carries it for.
|
|
406
415
|
const onChanged = (event) => {
|
|
407
416
|
const detail = event.detail
|
|
408
|
-
if (!detail || detail.pageRoot !== this.ctx.pageRoot || !detail.path) return
|
|
417
|
+
if (!detail || detail.pageRoot !== contentRoot(this.ctx.pageRoot) || !detail.path) return
|
|
409
418
|
for (const binding of bindings.values()) {
|
|
410
419
|
if (binding.path === detail.path) binding.written = true
|
|
411
420
|
}
|
|
@@ -448,6 +457,7 @@ export function createInlineView({ doc, pageRoot, opts = {} }) {
|
|
|
448
457
|
activeTarget = target
|
|
449
458
|
activeHandle = handle || null
|
|
450
459
|
popMode = null
|
|
460
|
+
host.popTitleEl.textContent = 'Edit content'
|
|
451
461
|
|
|
452
462
|
// Unhide BEFORE measuring. A hidden popover has a zero rect, which places
|
|
453
463
|
// it at the clamp instead of beside its anchor — the same trap as the 0px
|
|
@@ -458,6 +468,33 @@ export function createInlineView({ doc, pageRoot, opts = {} }) {
|
|
|
458
468
|
focusFirst(leaf)
|
|
459
469
|
},
|
|
460
470
|
|
|
471
|
+
// A style popover shows exactly one group's style fields: the page's from
|
|
472
|
+
// the bar, a section's from its chip, a row's from its gear menu. The
|
|
473
|
+
// section and row ones sit beside what they style; the page one beside the
|
|
474
|
+
// button, because the whole page is no place to anchor a popover.
|
|
475
|
+
openStyle(group, handle) {
|
|
476
|
+
if (!group || !host) return
|
|
477
|
+
const leaf = revealPaths(this, host, group.paths)
|
|
478
|
+
if (!leaf) {
|
|
479
|
+
this.deactivate()
|
|
480
|
+
return
|
|
481
|
+
}
|
|
482
|
+
activeTarget = {
|
|
483
|
+
kind: 'style',
|
|
484
|
+
scope: group.scope,
|
|
485
|
+
el: group.scope === 'page' ? host.pageStyleEl : group.el,
|
|
486
|
+
path: group.paths[0].split('.'),
|
|
487
|
+
paths: group.paths,
|
|
488
|
+
}
|
|
489
|
+
activeHandle = handle || null
|
|
490
|
+
popMode = null
|
|
491
|
+
host.popTitleEl.textContent = group.scope === 'page' ? 'Page style' : 'Style'
|
|
492
|
+
host.popEl.hidden = false
|
|
493
|
+
host.formRoot.querySelectorAll(`.${ACTIVE_CLASS}`).forEach(autosizeIn)
|
|
494
|
+
this.placePopover()
|
|
495
|
+
focusFirst(leaf)
|
|
496
|
+
},
|
|
497
|
+
|
|
461
498
|
// Bind richclay to the page element and edit the text where it sits.
|
|
462
499
|
// Returns false when the target cannot be bound, which is the caller's
|
|
463
500
|
// signal to open the popover over it instead.
|
|
@@ -511,13 +548,23 @@ export function createInlineView({ doc, pageRoot, opts = {} }) {
|
|
|
511
548
|
syncTargets() {
|
|
512
549
|
if (!layer) return
|
|
513
550
|
const ctx = this.ctx
|
|
514
|
-
const
|
|
515
|
-
|
|
551
|
+
const resolved = resolveTargets(ctx.pageRoot, ctx.pageRules, doc)
|
|
552
|
+
const lists = resolved.lists
|
|
553
|
+
const targets = resolved.targets.filter((target) => !target.style)
|
|
554
|
+
groupsNow = styleGroups(resolved.targets, lists)
|
|
555
|
+
host.pageStyleEl.hidden = !groupsNow.some((group) => group.scope === 'page')
|
|
556
|
+
layer.setTargets(resolved.targets, lists, groupsNow)
|
|
516
557
|
const byEl = reconcileBindings(this, bindings, targets, doc)
|
|
517
558
|
// An open popover holds its path the same way a binding does. Re-point it
|
|
518
559
|
// at the target that now stands for the element it is anchored over, or
|
|
519
560
|
// the popover keeps editing the row that inherited its old index.
|
|
520
|
-
if (activeTarget) {
|
|
561
|
+
if (activeTarget?.kind === 'style') {
|
|
562
|
+
const group = activeTarget.scope === 'page'
|
|
563
|
+
? groupsNow.find((candidate) => candidate.scope === 'page')
|
|
564
|
+
: groupsNow.find((candidate) => candidate.el === activeTarget.el)
|
|
565
|
+
if (group) activeTarget = { ...activeTarget, paths: group.paths, path: group.paths[0].split('.') }
|
|
566
|
+
else this.deactivate()
|
|
567
|
+
} else if (activeTarget) {
|
|
521
568
|
const candidates = byEl.get(activeTarget.el) || []
|
|
522
569
|
const fresh = candidates.find((target) => sameTargetBinding(target, activeTarget)) ||
|
|
523
570
|
candidates.find((target) => target.path.join('.') === activeTarget.path.join('.')) ||
|
|
@@ -644,7 +691,9 @@ export function createInlineView({ doc, pageRoot, opts = {} }) {
|
|
|
644
691
|
this.deactivate()
|
|
645
692
|
return
|
|
646
693
|
}
|
|
647
|
-
const leaf =
|
|
694
|
+
const leaf = activeTarget.kind === 'style'
|
|
695
|
+
? revealPaths(this, host, activeTarget.paths)
|
|
696
|
+
: revealPath(this, host, activeTarget.path.join('.'))
|
|
648
697
|
if (!leaf) {
|
|
649
698
|
this.deactivate()
|
|
650
699
|
return
|
|
@@ -951,18 +1000,34 @@ function revealPath(view, host, path) {
|
|
|
951
1000
|
field.closest('[data-hcms-shape="object-array"], [data-hcms-shape="scalar-array"]') === item.parentElement.closest('[data-hcms-shape="object-array"]'))
|
|
952
1001
|
: [leaf]
|
|
953
1002
|
if (!fields.includes(leaf)) fields.push(leaf)
|
|
954
|
-
for (const field of fields)
|
|
955
|
-
field.classList.add(ACTIVE_CLASS)
|
|
956
|
-
field.removeAttribute('draggable')
|
|
957
|
-
for (let el = field.parentElement; el; el = el.parentElement) {
|
|
958
|
-
el.classList.add(ONPATH_CLASS)
|
|
959
|
-
el.removeAttribute('draggable')
|
|
960
|
-
if (el === view.formRoot) break
|
|
961
|
-
}
|
|
962
|
-
}
|
|
1003
|
+
for (const field of fields) markRevealed(view, field)
|
|
963
1004
|
return leaf
|
|
964
1005
|
}
|
|
965
1006
|
|
|
1007
|
+
// Reveal exactly these fields, with no card siblings: a style popover shows
|
|
1008
|
+
// style fields only.
|
|
1009
|
+
function revealPaths(view, host, paths) {
|
|
1010
|
+
clearPathClasses(host.root)
|
|
1011
|
+
let first = null
|
|
1012
|
+
for (const path of paths) {
|
|
1013
|
+
const field = view.formRoot && view.formRoot.querySelector(`[data-hcms-path="${cssEscape(path)}"]`)
|
|
1014
|
+
if (!field) continue
|
|
1015
|
+
first = first || field
|
|
1016
|
+
markRevealed(view, field)
|
|
1017
|
+
}
|
|
1018
|
+
return first
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
function markRevealed(view, field) {
|
|
1022
|
+
field.classList.add(ACTIVE_CLASS)
|
|
1023
|
+
field.removeAttribute('draggable')
|
|
1024
|
+
for (let el = field.parentElement; el; el = el.parentElement) {
|
|
1025
|
+
el.classList.add(ONPATH_CLASS)
|
|
1026
|
+
el.removeAttribute('draggable')
|
|
1027
|
+
if (el === view.formRoot) break
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
|
|
966
1031
|
function clearPathClasses(root) {
|
|
967
1032
|
if (!root) return
|
|
968
1033
|
for (const el of root.querySelectorAll(`.${ACTIVE_CLASS}, .${ONPATH_CLASS}`)) {
|
|
@@ -1036,6 +1101,9 @@ function mountInlineHost(doc, theme) {
|
|
|
1036
1101
|
<span class="mirk-button__label">Edit in the sidebar</span>
|
|
1037
1102
|
</button>
|
|
1038
1103
|
</div>
|
|
1104
|
+
<button type="button" class="hcms-inline-page-style mirk-button mirk-button--small" data-hcms-page-style-open hidden>
|
|
1105
|
+
<span class="mirk-button__label">Page style</span>
|
|
1106
|
+
</button>
|
|
1039
1107
|
<button type="button" class="hcms-inline-toggle mirk-button mirk-button--small" data-hcms-controls-toggle aria-pressed="false" hidden>
|
|
1040
1108
|
<span class="mirk-button__label">Hide controls</span>
|
|
1041
1109
|
</button>
|
|
@@ -1045,7 +1113,7 @@ function mountInlineHost(doc, theme) {
|
|
|
1045
1113
|
<div class="hcms-inline-layer"></div>
|
|
1046
1114
|
<div class="hcms-inline-pop" role="dialog" aria-label="Edit content" hidden>
|
|
1047
1115
|
<div class="hcms-inline-pop-header">
|
|
1048
|
-
<span>Edit content</span>
|
|
1116
|
+
<span class="hcms-inline-pop-title">Edit content</span>
|
|
1049
1117
|
<button type="button" class="hcms-inline-pop-close mirk-button mirk-button--small" aria-label="Close field editor">
|
|
1050
1118
|
<span class="mirk-button__label">${inlineIcon('remove')}</span>
|
|
1051
1119
|
</button>
|
|
@@ -1067,6 +1135,8 @@ function mountInlineHost(doc, theme) {
|
|
|
1067
1135
|
popEl: root.querySelector('.hcms-inline-pop'),
|
|
1068
1136
|
closeEl: root.querySelector('.hcms-inline-pop-close'),
|
|
1069
1137
|
toggleEl: root.querySelector('[data-hcms-controls-toggle]'),
|
|
1138
|
+
pageStyleEl: root.querySelector('[data-hcms-page-style-open]'),
|
|
1139
|
+
popTitleEl: root.querySelector('.hcms-inline-pop-title'),
|
|
1070
1140
|
destroy() { root.remove() },
|
|
1071
1141
|
}
|
|
1072
1142
|
}
|