@webx-ui/module-banners 0.2.0 → 0.2.2
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/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -972,6 +972,7 @@ const Sa = /* @__PURE__ */ Ve({
|
|
|
972
972
|
modelValue: w.value,
|
|
973
973
|
"onUpdate:modelValue": M[2] || (M[2] = (D) => w.value = D),
|
|
974
974
|
class: oe(["wx-banner-place__list", { "is-loading": g.value }]),
|
|
975
|
+
hoverable: "",
|
|
975
976
|
plain: "",
|
|
976
977
|
"item-key": "id",
|
|
977
978
|
handle: J.value ? "grip" : "row",
|
|
@@ -1044,7 +1045,7 @@ const Sa = /* @__PURE__ */ Ve({
|
|
|
1044
1045
|
], 64))
|
|
1045
1046
|
]));
|
|
1046
1047
|
}
|
|
1047
|
-
}), Ea = /* @__PURE__ */ Ie(Da, [["__scopeId", "data-v-
|
|
1048
|
+
}), Ea = /* @__PURE__ */ Ie(Da, [["__scopeId", "data-v-48d645bf"]]), Ma = {
|
|
1048
1049
|
key: 2,
|
|
1049
1050
|
class: "wx-banners__list"
|
|
1050
1051
|
}, Aa = ["onClick"], Ra = { class: "wx-banners__name" }, Fa = /* @__PURE__ */ Ve({
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/api.ts","../src/editor.ts","../src/layers.ts","../src/messages.ts","../src/i18n.ts","../src/BannerEditorPage.vue","../src/PlaceDialog.vue","../src/PlacePane.vue","../src/BannersPage.vue","../src/LayerInspector.vue","../src/LayersField.vue","../src/module.ts"],"sourcesContent":["import type { AdminContext } from '@webx-ui/module-admin'\nimport type { ScreenModel } from '@webx-ui/schema'\nimport type { BannerDetail, BannerRow, BannersTheme, PlaceInput, PlaceRow } from './types'\n\nexport interface BannersApi {\n /** Every place: the declared ones in the config's order, then the own ones by name. */\n places(): Promise<PlaceRow[]>\n createPlace(input: PlaceInput): Promise<PlaceRow>\n /** Only a place of somebody's own, and only its name: the key is what a template asks for. */\n renamePlace(key: string, title: PlaceInput['title']): Promise<PlaceRow>\n /** Only an own place with nothing in it, the bin included — a 422 with the count otherwise. */\n removePlace(key: string): Promise<void>\n\n /** The banners of a place in their order, or its bin. A declared place without a row: `[]`. */\n list(place: string, trashed?: boolean): Promise<BannerRow[]>\n get(id: number): Promise<BannerDetail>\n /**\n * A new banner out of the values of its form, in one request: the form is filled in first and\n * the record made on the first save, so nobody leaves an empty banner in a place.\n */\n create(place: string, values: ScreenModel): Promise<BannerDetail>\n /**\n * Refused with a 422 under the name of the field — `buttons.<n>.link` for a button's row. A\n * `place` other than the one it stands in moves it to the end of that one (decision 14).\n */\n save(id: number, values: ScreenModel, place?: string): Promise<BannerDetail>\n remove(id: number): Promise<void>\n restore(id: number): Promise<BannerRow>\n /** The order of one place, written whole. */\n reorder(place: string, ids: number[]): Promise<void>\n /** The site's theme as the layer editor shows it; empty on a site without one. */\n theme(): Promise<BannersTheme>\n}\n\n/** Everything under `/banners`, below the panel's API path (§5.6). */\nexport function createBannersApi(admin: AdminContext): BannersApi {\n const base = `${admin.apiPath}/banners`\n const place = (key: string) => `${base}/places/${encodeURIComponent(key)}`\n const data = <T>(body: { data: T }): T => body.data\n\n return {\n places: () => admin.http.get<{ data: PlaceRow[] }>(`${base}/places`).then(data),\n createPlace: (input) => admin.http.post<{ data: PlaceRow }>(`${base}/places`, input).then(data),\n renamePlace: (key, title) =>\n admin.http.put<{ data: PlaceRow }>(place(key), { title }).then(data),\n removePlace: (key) => admin.http.delete<void>(place(key)).then(() => undefined),\n\n list: (key, trashed = false) =>\n admin.http\n .get<{ data: BannerRow[] }>(`${place(key)}/banners${trashed ? '?trashed=1' : ''}`)\n .then(data),\n get: (id) => admin.http.get<{ data: BannerDetail }>(`${base}/${id}`).then(data),\n create: (key, values) =>\n admin.http.post<{ data: BannerDetail }>(`${place(key)}/banners`, { values }).then(data),\n save: (id, values, key) =>\n admin.http\n .put<{ data: BannerDetail }>(\n `${base}/${id}`,\n key === undefined ? { values } : { values, place: key },\n )\n .then(data),\n remove: (id) => admin.http.delete<void>(`${base}/${id}`).then(() => undefined),\n // The resource of the list line, and not the form's pair: a restored banner stays closed.\n restore: (id) => admin.http.post<{ data: BannerRow }>(`${base}/${id}/restore`, {}).then(data),\n reorder: (key, ids) =>\n admin.http.post<void>(`${place(key)}/reorder`, { ids }).then(() => undefined),\n theme: () => admin.http.get<{ data: BannersTheme }>(`${base}/theme`).then(data),\n }\n}\n","import { inject, provide, type ComputedRef, type InjectionKey, type Ref } from 'vue'\nimport type { ScreenModel } from '@webx-ui/schema'\nimport type { PlaceRow } from './types'\n\n/**\n * What the banner's page tells the fields of its screen that need more than their own value: the\n * layer editor draws the banner's picture, puts in its title and text, and lays out its buttons,\n * in the frame of the place the banner stands in.\n */\nexport interface BannerEditorContext {\n values: Ref<ScreenModel>\n place: ComputedRef<PlaceRow | null>\n}\n\nconst key: InjectionKey<BannerEditorContext> = Symbol('wx-banner-editor')\n\nexport function provideBannerEditor(context: BannerEditorContext): void {\n provide(key, context)\n}\n\nexport function useBannerEditor(): BannerEditorContext | null {\n return inject(key, null)\n}\n","import type { LocalizedValue } from '@webx-ui/core'\nimport type { LinkValue } from '@webx-ui/module-admin'\n\n/**\n * The layers of a banner on the panel's side (the layers spec, §3.2): their shape, the ones a\n * banner starts with, and the custom properties `layers.css` reads — the same numbers the server\n * prints (`Rendering\\Layers`), so the canvas is the site.\n */\n\nexport type LayerType = 'text' | 'image' | 'button' | 'buttons'\n\nexport type LayerOrigin = 'tl' | 't' | 'tr' | 'l' | 'c' | 'r' | 'bl' | 'b' | 'br'\n\nexport type LayerView = 'desktop' | 'mobile'\n\n/** One layout's geometry, every length in percent of the picture's width (decision 1). */\nexport interface LayerFrame {\n x: number\n y: number\n w: number\n size: number\n pad: number\n radius: number\n /** In em: letter spacing follows the type, not the picture. */\n tracking: number\n rotate: number\n gap: number\n origin: LayerOrigin\n hidden: boolean\n}\n\nexport interface LayerStyle {\n font: 'heading' | 'body' | null\n weight: number\n italic: boolean\n uppercase: boolean\n align: 'start' | 'center' | 'end'\n line_height: number\n /** `#rrggbb`, `token:<name of the palette>` or `null` — the site's. */\n color: string | null\n background: string | null\n opacity: number\n shadow: boolean\n}\n\nexport interface LayerMotion {\n in: 'fade' | 'rise' | 'slide-start' | 'slide-end' | 'zoom' | null\n loop: 'spin' | 'float' | null\n delay: number\n duration: number\n}\n\nexport interface MediaRef {\n path: string\n url?: string\n alt?: LocalizedValue | string\n}\n\nexport interface BannerLayer {\n id: string\n type: LayerType\n name?: string\n text?: LocalizedValue\n tag?: string\n image?: MediaRef | null\n link?: LinkValue | null\n variant?: string | null\n style: LayerStyle\n frames: { desktop: LayerFrame; mobile: LayerFrame | null }\n motion: LayerMotion\n}\n\n/** Range, step and default of each number of a layout — as `LayerRules::FRAME` has them. */\nexport const FRAME_LIMITS: Record<\n Exclude<keyof LayerFrame, 'origin' | 'hidden'>,\n [number, number, number]\n> = {\n x: [-50, 150, 5],\n y: [-50, 150, 5],\n w: [1, 150, 40],\n size: [0.3, 20, 3],\n pad: [0, 10, 0],\n radius: [0, 50, 0],\n tracking: [-0.2, 1, 0],\n rotate: [-180, 180, 0],\n gap: [0, 10, 1],\n}\n\nexport const ORIGINS: LayerOrigin[] = ['tl', 't', 'tr', 'l', 'c', 'r', 'bl', 'b', 'br']\n\nconst ORIGIN_SHIFT: Record<LayerOrigin, [number, number]> = {\n tl: [0, 0],\n t: [-50, 0],\n tr: [-100, 0],\n l: [0, -50],\n c: [-50, -50],\n r: [-100, -50],\n bl: [0, -100],\n b: [-50, -100],\n br: [-100, -100],\n}\n\nexport const ENTRANCES = ['fade', 'rise', 'slide-start', 'slide-end', 'zoom'] as const\n\nexport const LOOPS = ['spin', 'float'] as const\n\n/** The two fields of a banner a layer's words may put in. */\nexport const FIELDS = ['title', 'text'] as const\n\nexport function frame(over: Partial<LayerFrame> = {}): LayerFrame {\n return {\n x: 5,\n y: 5,\n w: 40,\n size: 3,\n pad: 0,\n radius: 0,\n tracking: 0,\n rotate: 0,\n gap: 1,\n origin: 'tl',\n hidden: false,\n ...over,\n }\n}\n\nexport function style(over: Partial<LayerStyle> = {}): LayerStyle {\n return {\n font: null,\n weight: 400,\n italic: false,\n uppercase: false,\n align: 'start',\n line_height: 1.2,\n color: null,\n background: null,\n opacity: 1,\n shadow: false,\n ...over,\n }\n}\n\nexport function motion(over: Partial<LayerMotion> = {}): LayerMotion {\n return { in: null, loop: null, delay: 0, duration: 600, ...over }\n}\n\n/**\n * A deep copy of a layer. Not `structuredClone`: what the form holds is a reactive proxy, and the\n * browser refuses to clone one.\n */\nexport function clone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T\n}\n\n/** A short id, unique among the layers it joins. */\nexport function layerId(taken: Iterable<string> = []): string {\n const used = new Set(taken)\n let id = ''\n\n do {\n id = Math.random().toString(36).slice(2, 8).padEnd(6, '0')\n } while (used.has(id) || !/^[a-z0-9]{4,12}$/.test(id))\n\n return id\n}\n\n/** A new layer of a kind, where a new layer of that kind usually goes. */\nexport function newLayer(type: LayerType, taken: Iterable<string>, locale: string): BannerLayer {\n const base = { id: layerId(taken), type, style: style(), motion: motion() }\n\n switch (type) {\n case 'text':\n return {\n ...base,\n text: { [locale]: 'Text' },\n tag: 'p',\n frames: { desktop: frame({ x: 10, y: 10, size: 2.4 }), mobile: null },\n }\n case 'image':\n return {\n ...base,\n image: null,\n frames: { desktop: frame({ x: 60, y: 10, w: 20 }), mobile: null },\n }\n case 'button':\n return {\n ...base,\n text: { [locale]: 'Button' },\n link: null,\n variant: null,\n frames: { desktop: frame({ x: 10, y: 25, w: 30, size: 1.2 }), mobile: null },\n }\n case 'buttons':\n return {\n ...base,\n frames: { desktop: frame({ x: 6, y: 27, w: 50, size: 1.2 }), mobile: null },\n }\n }\n}\n\n/**\n * What a banner of a place with layers starts with (decision 5): its title, its text and its\n * buttons, in a column on the left. The words are written once, in the default language — the\n * others take them from there (decision 6) — and the fields are put in in the page's language.\n */\nexport function defaultLayers(defaultLocale: string): BannerLayer[] {\n const taken: string[] = []\n const next = (layer: Omit<BannerLayer, 'id'>): BannerLayer => {\n const made = { ...layer, id: layerId(taken) } as BannerLayer\n\n taken.push(made.id)\n\n return made\n }\n\n return [\n next({\n type: 'text',\n text: { [defaultLocale]: '{title}' },\n tag: 'h2',\n style: style({ weight: 700, line_height: 1.05, color: '#ffffff', shadow: true }),\n frames: { desktop: frame({ x: 6, y: 6, w: 45, size: 4.2 }), mobile: null },\n motion: motion({ in: 'rise' }),\n }),\n next({\n type: 'text',\n text: { [defaultLocale]: '{text}' },\n tag: 'p',\n style: style({ line_height: 1.4, color: '#ffffff', shadow: true }),\n frames: { desktop: frame({ x: 6, y: 18, w: 40, size: 1.4 }), mobile: null },\n motion: motion({ in: 'rise', delay: 150 }),\n }),\n next({\n type: 'buttons',\n style: style(),\n frames: { desktop: frame({ x: 6, y: 28, w: 50, size: 1.2 }), mobile: null },\n motion: motion({ in: 'rise', delay: 300 }),\n }),\n ]\n}\n\n/**\n * A layer's words in this language: the page's, else the site's default, else the first one\n * written in the order of the site's languages (decision 6) — then the banner's fields put in,\n * in the page's language. The same as `Rendering\\Layers::words()`.\n */\nexport function layerWords(\n text: LocalizedValue | undefined,\n locale: string,\n order: string[],\n fields: Partial<Record<(typeof FIELDS)[number], string>>,\n): string {\n const map = text ?? {}\n const codes = [...new Set([locale, ...order, ...Object.keys(map)])]\n const words = codes.map((code) => map[code]).find((one) => (one ?? '').trim() !== '') ?? ''\n\n return words\n .replaceAll('{title}', fields.title ?? '')\n .replaceAll('{text}', fields.text ?? '')\n .trim()\n}\n\n/** `{title}` and `{text}` are fields; any other `{…}` in a layer's words is very likely a typo. */\nexport function unknownFields(text: string): string[] {\n return [...text.matchAll(/\\{([a-z_.]+)\\}/g)]\n .map((match) => match[1] ?? '')\n .filter((name) => !(FIELDS as readonly string[]).includes(name))\n}\n\n/** A colour of the theme's palette, or one of its own; anything else is no colour at all. */\nexport function cssColor(color: string | null | undefined): string | null {\n if (typeof color !== 'string') return null\n if (/^#[0-9a-f]{6}([0-9a-f]{2})?$/.test(color)) return color\n\n const token = /^token:([a-z][a-z0-9-]{0,40})$/.exec(color)\n\n return token ? `var(--site-color-${token[1]})` : null\n}\n\nconst round = (value: number): number => Math.round(value * 100) / 100\n\n/** The custom properties of a layer, as the server prints them, for both layouts. */\nexport function layerVars(layer: BannerLayer, z: number): Record<string, string> {\n const vars: Record<string, string> = {}\n const desktop = layer.frames.desktop\n const mobile = layer.frames.mobile ?? desktop\n\n for (const [prefix, one] of [\n ['', desktop],\n ['m-', mobile],\n ] as const) {\n for (const key of Object.keys(FRAME_LIMITS) as (keyof typeof FRAME_LIMITS)[]) {\n vars[`--${prefix}${key}`] = String(round(one[key]))\n }\n\n const [ox, oy] = ORIGIN_SHIFT[one.origin] ?? [0, 0]\n\n vars[`--${prefix}ox`] = `${ox}%`\n vars[`--${prefix}oy`] = `${oy}%`\n vars[`--${prefix}display`] = one.hidden ? 'none' : 'block'\n }\n\n vars['--weight'] = String(layer.style.weight)\n vars['--leading'] = String(layer.style.line_height)\n vars['--opacity'] = String(layer.style.opacity)\n vars['--align'] = layer.style.align\n\n const color = cssColor(layer.style.color)\n const background = cssColor(layer.style.background)\n\n if (color) vars['--color'] = color\n if (background) vars['--background'] = background\n if (layer.style.font) vars['--font'] = `var(--site-font-${layer.style.font})`\n\n vars['z-index'] = String(z)\n\n return vars\n}\n\n/** The classes of a layer's element, as the server prints them (motion is the site's, not the canvas's). */\nexport function layerClasses(layer: BannerLayer): string[] {\n const classes = ['webx-banner__layer', `webx-banner__layer--${layer.type}`]\n\n if (layer.style.italic) classes.push('is-italic')\n if (layer.style.uppercase) classes.push('is-uppercase')\n if (layer.style.shadow) classes.push('is-shadow')\n if (layer.style.background) classes.push('has-background')\n\n return classes\n}\n\n/** `16/6`, `16 / 6`, `2.4` → width over height; anything else is `fallback`. */\nexport function ratioOf(value: string | number | null | undefined, fallback: number): number {\n if (typeof value === 'number') return value > 0 ? value : fallback\n\n const match = /^\\s*(\\d+(?:\\.\\d+)?)\\s*(?:\\/\\s*(\\d+(?:\\.\\d+)?))?\\s*$/.exec(value ?? '')\n\n if (!match) return fallback\n\n const ratio = Number(match[1]) / Number(match[2] ?? 1)\n\n return Number.isFinite(ratio) && ratio > 0 ? ratio : fallback\n}\n\n/**\n * Where the frame of the place falls on the picture — what the site shows of it — as percent of\n * the picture's width and height. `cover` crops the longer side, centred.\n */\nexport function cropBox(\n image: number,\n frameRatio: number,\n): { left: number; top: number; width: number; height: number } {\n if (image > frameRatio) {\n const width = (frameRatio / image) * 100\n\n return { left: (100 - width) / 2, top: 0, width, height: 100 }\n }\n\n const height = (image / frameRatio) * 100\n\n return { left: 0, top: (100 - height) / 2, width: 100, height }\n}\n\n/** A layer as it is stored: the phone's layout, when it differs, or `null`. */\nexport function clampFrame(one: LayerFrame): LayerFrame {\n const next = { ...one }\n\n for (const key of Object.keys(FRAME_LIMITS) as (keyof typeof FRAME_LIMITS)[]) {\n const [min, max] = FRAME_LIMITS[key]\n\n next[key] = round(Math.min(max, Math.max(min, Number(next[key]) || 0)))\n }\n\n return next\n}\n","import type { Messages } from '@webx-ui/module-admin'\n\n/**\n * The English this package speaks on its own, before the server's dictionary arrives, or without\n * one. The server ships the same lines in ten languages under `webx-banners::` and overrides\n * these; `messages.test.ts` keeps the two sets of keys equal. The server's refusals (`errors`)\n * are not here: the panel only ever shows them as they come.\n */\nexport const bannersMessages: Record<string, Messages> = {\n module: {\n banners: 'Banners',\n },\n places: {\n hero: 'Home page slider',\n promo: 'Promo strip',\n notice: 'Announcement bar',\n list: 'Places',\n new: 'New place',\n title: 'Name',\n key: 'Key',\n 'key-help':\n 'What the site’s template asks for: lowercase Latin letters, digits and dashes, starting with a letter. It cannot be changed later.',\n declared: 'Declared in the configuration',\n 'in-template': \"In a template: banners(':key')\",\n count: 'Banners',\n rename: 'Rename',\n delete: 'Delete',\n 'delete-confirm': 'Delete the place “:title”?',\n 'delete-not-empty': 'Only an empty place can be deleted — the bin counts too.',\n empty: 'No places yet',\n choose: 'Choose a place on the left',\n create: 'Create',\n save: 'Save',\n cancel: 'Cancel',\n created: 'Place created',\n renamed: 'Place renamed',\n deleted: 'Place deleted',\n places: 'Places',\n 'new-title': 'New place',\n 'rename-title': 'Rename the place',\n 'delete-title': 'Delete the place “:place”?',\n 'delete-text':\n 'There is nothing in it. A template that still asks for it gets an empty list, not an error.',\n 'delete-empty-first': 'Delete: empty it first',\n 'delete-blocked': 'Banners in this place, the bin included: :count. Take them out first.',\n 'field-title': 'Name',\n 'field-key': 'Key',\n 'declared-help': 'Cannot be renamed or deleted here: a template asks for it by its key.',\n usage: 'In a template:',\n 'layout-single': 'One banner',\n 'layout-random': 'A random one',\n 'layout-slider': 'Slider',\n 'empty-help': 'The site’s config declares the places its templates ask for.',\n 'choose-help': 'Its banners open beside the list.',\n },\n banner: {\n new: 'New banner',\n trash: 'Bin',\n 'to-trash': 'Move to the bin',\n restore: 'Restore',\n open: 'Open',\n enable: 'Turn on',\n disable: 'Turn off',\n disabled: 'Off',\n video: 'Video',\n empty: 'No banners in this place yet',\n 'trash-empty': 'The bin is empty',\n place: 'Place',\n 'place-help': 'Moving the banner puts it at the end of the new place.',\n back: 'Back to the place',\n save: 'Save',\n saved: 'Banner saved',\n trashed: 'Bin',\n restored: 'Banner restored',\n 'leave-title': 'Leave without saving?',\n 'leave-text': 'The changes to this banner will be lost.',\n leave: 'Leave',\n stay: 'Stay',\n reorder: 'Reorder',\n untitled: 'Untitled banner',\n all: 'Banners',\n 'empty-help': 'The site shows them wherever a template asks for this place.',\n 'empty-bin': 'The bin is empty.',\n 'no-picture': 'No picture',\n order: 'Drag to change the order on the site.',\n 'switched-on': 'Switched on.',\n 'switched-off': 'Switched off.',\n delete: 'Delete',\n 'delete-title': 'Delete “:name”?',\n 'delete-text': 'It goes to the bin and leaves the site. Restored, it comes back to its place.',\n deleted: 'Moved to the bin.',\n 'reorder-failed': 'The new order was not saved.',\n 'save-failed': 'Not saved.',\n cancel: 'Cancel',\n previous: 'Previous banner',\n next: 'Next banner',\n },\n screen: {\n media: 'Pictures and video',\n image: 'Picture',\n 'image-help': 'Required. Shown on wide screens, and as the poster of the video.',\n 'image-mobile': 'Picture for phones',\n 'image-mobile-help': 'Optional. Narrow screens show it instead of the picture above.',\n video: 'Video',\n 'video-help':\n 'Optional. Plays silently over the picture; the picture becomes its poster and what is seen without it.',\n words: 'Words',\n title: 'Title',\n 'title-help':\n 'A banner with words is shown only in the languages they are written in; one without words is shown everywhere.',\n text: 'Text',\n 'text-help': 'Plain text; line breaks are kept.',\n buttons: 'Buttons',\n 'buttons-help': 'Up to three. A button without a label in a language is not shown in it.',\n 'buttons-add': 'Add a button',\n 'buttons-empty': 'No buttons',\n label: 'Label',\n link: 'Link',\n variant: 'Look',\n 'variant-placeholder': 'The first one',\n settings: 'Settings',\n enabled: 'On',\n 'enabled-help': 'Only a banner that is on is shown on the site. A new one starts off.',\n layers: 'Layers',\n 'layers-help':\n 'Texts, pictures and buttons placed on the picture. Every size is a percent of the picture’s width, so the banner scales as a whole.',\n },\n layers: {\n text: 'Text',\n image: 'Picture',\n button: 'Button',\n 'title-layer': 'Banner title',\n 'text-layer': 'Banner text',\n 'buttons-layer': 'Banner buttons',\n desktop: 'Computer',\n mobile: 'Phone',\n layout: 'Layout',\n undo: 'Undo (Ctrl+Z)',\n redo: 'Redo (Ctrl+Shift+Z)',\n 'not-here': 'This place does not print layers: they are kept, but the site does not show them.',\n canvas: 'Banner canvas',\n 'no-picture': 'Choose a picture above',\n hint: 'Click to select, drag to move, the handles resize. Arrows move by 0.05 (with Shift by 0.5), Delete removes, Ctrl+D duplicates, Ctrl+Z undoes. Alt while dragging: no snapping. The shade is what the frame of the place crops away.',\n empty: 'No layers yet',\n list: 'Layers',\n outside: 'Outside the frame: not seen on the site',\n show: 'Show on this layout',\n hide: 'Hide on this layout',\n delete: 'Delete',\n choose: 'Select a layer on the picture or in the list.',\n name: 'Name in the list',\n label: 'Label',\n words: 'Words',\n 'falls-back': 'Empty here: the site shows the words in :code.',\n 'words-help':\n 'Empty in a language — the default language is shown. {title} and {text} put in the banner’s fields.',\n 'unknown-field': 'Not a field of the banner: :fields. Fields are {title} and {text}.',\n tag: 'Tag',\n link: 'Link',\n variant: 'Look',\n 'buttons-help': 'The buttons of the banner, in a row. Edit them in “Buttons” below.',\n 'geometry-desktop': 'Position on a computer',\n 'geometry-mobile-own': 'Position on a phone',\n 'geometry-mobile-same': 'Position on a phone — as on a computer',\n 'same-as-desktop': 'As on a computer',\n unit: 'In percent of the picture’s width.',\n x: 'x (from the left)',\n y: 'y (from the top)',\n w: 'Width',\n size: 'Font size',\n pad: 'Padding',\n radius: 'Rounding',\n tracking: 'Letter spacing, em',\n rotate: 'Rotation, °',\n gap: 'Gap',\n origin: 'Anchor',\n 'origin-help': 'Which point of the layer stands at x and y: longer words grow away from it.',\n 'hidden-here': 'Hidden on this layout',\n look: 'Look',\n font: 'Font',\n 'font-site': 'As on the site',\n 'font-heading': 'Headings',\n 'font-body': 'Body text',\n weight: 'Weight',\n 'line-height': 'Line height',\n opacity: 'Opacity',\n align: 'Alignment',\n 'align-start': 'Start',\n 'align-center': 'Centre',\n 'align-end': 'End',\n italic: 'Italic',\n uppercase: 'Capitals',\n shadow: 'Shadow',\n color: 'Colour',\n background: 'Background',\n motion: 'Motion',\n entrance: 'Entrance',\n loop: 'Loop',\n delay: 'Delay, ms',\n duration: 'Duration, ms',\n 'motion-none': 'None',\n 'in-fade': 'Fade in',\n 'in-rise': 'Rise',\n 'in-slide-start': 'From the start',\n 'in-slide-end': 'From the end',\n 'in-zoom': 'Zoom',\n 'loop-spin': 'Spin',\n 'loop-float': 'Float',\n },\n}\n","import { useAdmin } from '@webx-ui/module-admin'\nimport { bannersMessages } from './messages'\n\nconst seeded = new WeakSet<object>()\n\n/** Puts this package's English under the panel's dictionary, once per panel. */\nexport function useBannersMessages(): void {\n try {\n const { i18n } = useAdmin()\n\n if (!seeded.has(i18n)) {\n i18n.defaults('webx-banners', bannersMessages)\n seeded.add(i18n)\n }\n } catch {\n // Outside a panel there is no dictionary to seed, and `useTranslate` falls back on its own.\n }\n}\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport { onBeforeRouteLeave, useRoute, useRouter } from 'vue-router'\nimport {\n confirm,\n localizedValue,\n toast,\n useLocales,\n WxActionBar,\n WxBadge,\n WxButton,\n WxCard,\n WxFormItem,\n WxSelect,\n WxSkeleton,\n type LocalizedValue,\n} from '@webx-ui/core'\nimport {\n useAdmin,\n useErrorText,\n useTranslate,\n WxSaveState,\n WxScreen,\n WxScreenHead,\n type ScreenAction,\n useBodyKeys,\n} from '@webx-ui/module-admin'\nimport type { Patch, ScreenModel } from '@webx-ui/schema'\nimport { createBannersApi } from './api'\nimport { provideBannerEditor } from './editor'\nimport { defaultLayers } from './layers'\nimport { useBannersMessages } from './i18n'\nimport type { BannerDetail, BannerSummary, PlaceRow } from './types'\n\n/**\n * One banner, on a page of its own: a head, the place, the described screen, and one button.\n *\n * A page and not a pane beside the list, because the form is long — three pictures, the words,\n * up to three buttons — and `banners.form` is a described screen a project adds its own fields\n * to (decision 14). One explicit save, no autosave: a banner has no draft (decision 13), so what\n * is saved is on the site at once, if it is switched on.\n *\n * The place is not a field of the screen (decision 9). The list of places is alive — own places\n * come and go — and the options of a described select are patched on when the server boots, so a\n * live list has nowhere to go there. It is a select of its own above the screen, and rides beside\n * the values: `PUT { values, place }`. Changing it marks the form changed like any field does.\n *\n * A new banner is a form first and a record on the first save: pressing \"New banner\" and walking\n * away should not leave an empty, switched-off row at the end of a place.\n */\ndefineOptions({ name: 'WxBannerEditorPage' })\n\nconst props = withDefaults(defineProps<{ base?: string }>(), { base: '/banners' })\n\nconst admin = useAdmin()\nconst api = createBannersApi(admin)\nconst route = useRoute()\nconst router = useRouter()\nconst locales = useLocales()\nuseBannersMessages()\n\nconst t = useTranslate('webx-banners')\n/* Not the server's `message`: the panel says how a request failed in its own words. */\nconst message = useErrorText()\n\n/** `null` — a banner not written yet (`/banners/new`). */\nconst id = computed<number | null>(() => {\n const raw = route.params.id\n\n return typeof raw === 'string' && raw !== '' ? Number(raw) : null\n})\n\nconst banner = ref<BannerSummary | null>(null)\nconst values = ref<ScreenModel>({})\nconst snapshot = ref('')\nconst place = ref<string | null>(null)\n/** The place the banner stands in on the server — what \"changed\" and \"back\" are measured by. */\nconst placeSaved = ref<string | null>(null)\nconst places = ref<PlaceRow[]>([])\n\nconst loading = ref(true)\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\n\nconst canManage = computed(() => admin.can('banners.manage'))\n\nconst current = computed(() => JSON.stringify(values.value))\nconst dirty = computed(\n () =>\n snapshot.value !== '' && (current.value !== snapshot.value || place.value !== placeSaved.value),\n)\n\nconst state = computed<'saving' | 'unsaved' | 'saved'>(() => {\n if (saving.value) return 'saving'\n\n return dirty.value || id.value === null ? 'unsaved' : 'saved'\n})\n\n/** The title follows the field rather than the answer: an edit shows at the top as it is typed. */\nconst title = computed(() => {\n const written = values.value.title as LocalizedValue | string | null | undefined\n const fallback = id.value === null ? t('banner.new') : (banner.value?.title ?? '')\n\n return localizedValue(written, locales.active.value, fallback) || t('banner.untitled')\n})\n\n/** Back to the place the banner stands in, and not to the section's first one. */\nconst back = computed(() => {\n const key = placeSaved.value ?? place.value\n\n return key === null ? props.base : `${props.base}?place=${encodeURIComponent(key)}`\n})\n\nconst section = computed(\n () =>\n admin.state.manifest?.modules.find((module) => module.id === 'banners')?.title ??\n t('module.banners'),\n)\n\n/** The place the banner stands in now — the select's, not the server's — with its layers' frame. */\nconst placeRow = computed(() => places.value.find((one) => one.key === place.value) ?? null)\n\nprovideBannerEditor({ values, place: placeRow })\n\n/* The layers' card only where the place prints layers (§4.4 of the layers spec). */\nconst screenPatch = computed<Patch>(() =>\n placeRow.value?.layers ? [] : [{ op: 'remove', target: 'layers-card' }],\n)\n\n/**\n * A banner of a place with layers that was never laid out starts with its title, its text and its\n * buttons as three layers (decision 5). `[]` is somebody having taken them all away, and stays.\n */\nfunction withDefaultLayers(model: ScreenModel): ScreenModel {\n if (!placeRow.value?.layers || (model.layers !== null && model.layers !== undefined)) return model\n\n return { ...model, layers: defaultLayers(locales.list.value[0]?.code ?? locales.active.value) }\n}\n\nconst placeOptions = computed(() =>\n places.value.map((one) => ({ value: one.key, label: `${one.title} · ${one.key}` })),\n)\n\nconst placeModel = computed<string | number | null>({\n get: () => place.value,\n set: (value) => {\n place.value = value === null || value === undefined ? null : String(value)\n },\n})\n\n/** What the server would answer for a banner with nothing in it yet (§5.6). */\nfunction blank(): ScreenModel {\n return {\n image: null,\n image_mobile: null,\n video: null,\n title: {},\n text: {},\n buttons: [],\n layers: null,\n enabled: false,\n }\n}\n\nfunction take(detail: BannerDetail): void {\n banner.value = detail.banner\n place.value = detail.banner.place\n placeSaved.value = detail.banner.place\n\n // The default layers are part of what the banner opens with, not a change somebody made.\n const opened = withDefaultLayers(detail.values)\n\n values.value = opened\n snapshot.value = JSON.stringify(opened)\n}\n\nasync function load(): Promise<void> {\n loading.value = true\n\n try {\n const [list, detail] = await Promise.all([\n api.places(),\n id.value === null ? Promise.resolve(null) : api.get(id.value),\n ])\n\n places.value = list\n\n if (detail !== null) {\n take(detail)\n } else {\n const asked = route.query.place\n const key = typeof asked === 'string' && list.some((one) => one.key === asked) ? asked : null\n banner.value = null\n place.value = key ?? list[0]?.key ?? null\n placeSaved.value = place.value\n\n const fresh = withDefaultLayers(blank())\n\n values.value = fresh\n snapshot.value = JSON.stringify(fresh)\n }\n } catch (error) {\n toast.danger(message(error))\n void router.push(props.base)\n } finally {\n loading.value = false\n }\n}\n\nasync function save(): Promise<void> {\n if (saving.value || !canManage.value || place.value === null) return\n\n saving.value = true\n errors.value = {}\n\n try {\n const made = id.value === null\n const detail = made\n ? await api.create(place.value, values.value)\n : await api.save(\n id.value as number,\n values.value,\n place.value !== placeSaved.value ? place.value : undefined,\n )\n\n take(detail)\n toast.success(t('banner.saved'))\n\n // A record now, with an address of its own; `replace`, so \"back\" does not reopen a blank form.\n if (made) void router.replace(`${props.base}/${detail.banner.id}`)\n } catch (error) {\n const body = (error as { body?: { errors?: Record<string, string[]> } }).body\n\n if (body?.errors) {\n errors.value = body.errors\n toast.danger(t('banner.save-failed'))\n } else {\n toast.danger(message(error))\n }\n } finally {\n saving.value = false\n }\n}\n\nasync function remove(): Promise<void> {\n const record = banner.value\n\n if (!record) return\n\n const agreed = await confirm({\n title: t('banner.delete-title', { name: record.title }),\n message: t('banner.delete-text'),\n confirmText: t('banner.delete'),\n cancelText: t('banner.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.remove(record.id)\n toast.success(t('banner.deleted'))\n // Nothing left to protect: the record is in the bin, and asking about its unsaved edits on\n // the way out would be asking about a banner that is gone.\n snapshot.value = current.value\n place.value = placeSaved.value\n void router.push(back.value)\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\n/* The browser's own guard: it cannot wait for a request, so all it can do is ask. */\nfunction leaveGuard(event: BeforeUnloadEvent): void {\n if (dirty.value) event.preventDefault()\n}\n\n/* Ctrl+S saves, as it does everywhere else that has a save button. */\nfunction onKeydown(event: KeyboardEvent): void {\n if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 's') {\n event.preventDefault()\n if (dirty.value || id.value === null) void save()\n }\n}\n\n// And from the page around the form, where a click on empty space leaves the focus.\nuseBodyKeys(onKeydown)\n\n/*\n * `/banners/new` and `/banners/5` are one component on two routes, and the router patches it\n * rather than mounting it again (CLAUDE.md §4) — so a change of banner is watched for. The first\n * save moving the address to the record it just made is not a change: that banner is on screen.\n */\n/* Moved to a place with layers, a banner never laid out gets the default three there too. */\nwatch(place, () => {\n if (!loading.value) values.value = withDefaultLayers(values.value)\n})\n\nwatch(id, (next) => {\n if (next !== null && banner.value?.id === next) return\n\n void load()\n})\n\nonMounted(() => {\n void load()\n window.addEventListener('beforeunload', leaveGuard)\n})\n\nonBeforeUnmount(() => {\n window.removeEventListener('beforeunload', leaveGuard)\n})\n\nonBeforeRouteLeave(async () => {\n if (!dirty.value || !canManage.value) return true\n\n return await confirm({\n title: t('banner.leave-title'),\n message: t('banner.leave-text'),\n confirmText: t('banner.leave'),\n cancelText: t('banner.cancel'),\n tone: 'danger',\n })\n})\n\n/* What leads away from the banner. On a phone the head folds it into the ···. */\nconst actions = computed<ScreenAction[]>(() =>\n canManage.value && banner.value !== null\n ? [\n {\n key: 'delete',\n label: t('banner.delete'),\n icon: 'trash',\n danger: true,\n menu: true,\n run: () => void remove(),\n },\n ]\n : [],\n)\n</script>\n\n<template>\n <div class=\"wx-banner-editor\" @keydown=\"onKeydown\">\n <template v-if=\"loading\">\n <!-- Shaped like the screen it stands in for: the head, and a card for what is coming. -->\n <wx-skeleton class=\"wx-banner-editor__ghost\" title :rows=\"1\" />\n <wx-card><wx-skeleton :rows=\"6\" /></wx-card>\n </template>\n\n <template v-else>\n <wx-screen-head divider :back=\"back\" :back-label=\"section\" :title=\"title\" :actions=\"actions\">\n <template v-if=\"banner && !banner.enabled\" #title-after>\n <wx-badge dot>{{ t('banner.disabled') }}</wx-badge>\n </template>\n </wx-screen-head>\n\n <wx-card class=\"wx-banner-editor__place\">\n <wx-form-item\n :label=\"t('banner.place')\"\n :help=\"id === null ? undefined : t('banner.place-help')\"\n :error=\"errors.place?.[0]\"\n required\n >\n <wx-select\n v-model=\"placeModel\"\n :options=\"placeOptions\"\n :disabled=\"!canManage || saving\"\n :aria-label=\"t('banner.place')\"\n />\n </wx-form-item>\n </wx-card>\n\n <wx-screen\n v-model=\"values\"\n name=\"banners.form\"\n :patch=\"screenPatch\"\n :errors=\"errors\"\n :disabled=\"!canManage || saving\"\n />\n\n <wx-action-bar v-if=\"canManage\">\n <template #state>\n <wx-save-state :state=\"state\" />\n </template>\n\n <wx-button\n type=\"primary\"\n :loading=\"saving\"\n :disabled=\"(!dirty && id !== null) || place === null\"\n @click=\"save\"\n >\n {{ t('banner.save') }}\n </wx-button>\n </wx-action-bar>\n </template>\n </div>\n</template>\n\n<style scoped>\n.wx-banner-editor {\n display: flex;\n flex-direction: column;\n gap: var(--wx-gap, var(--wx-space-16));\n container-type: inline-size;\n}\n\n.wx-banner-editor__ghost {\n max-width: 420px;\n}\n\n/* A select as wide as a place's name needs, not as the page: it is one choice, not a text. */\n.wx-banner-editor__place :deep(.wx-form-item) {\n max-width: 420px;\n margin-bottom: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { nextTick, onMounted, ref, useTemplateRef } from 'vue'\nimport {\n useLocales,\n useModal,\n WxButton,\n WxDialog,\n WxFormItem,\n WxInput,\n WxSpace,\n type LocalizedValue,\n} from '@webx-ui/core'\nimport { useAdmin, useTranslate } from '@webx-ui/module-admin'\nimport { createBannersApi } from './api'\nimport { useBannersMessages } from './i18n'\nimport type { PlaceRow } from './types'\n\n/**\n * A place of somebody's own: a name, and a key when it is new (§5.4).\n *\n * The key does not follow the name the way a page's address follows its title: it is typed into\n * `banners('…')` by hand, and a key that quietly became `promo-2` would be a template asking for\n * a place that is not there. For the same reason a rename never touches it.\n *\n * The name is translated, and required in the default language — which is what the list falls\n * back on in a panel open in a language nobody named the place in.\n */\ndefineOptions({ name: 'WxBannerPlaceDialog' })\n\nconst props = withDefaults(defineProps<{ place?: PlaceRow | null }>(), { place: null })\n\nconst { open, resolve, dismiss } = useModal<PlaceRow>()\n\nconst api = createBannersApi(useAdmin())\nconst locales = useLocales()\nuseBannersMessages()\n\nconst t = useTranslate('webx-banners')\n\nconst editing = props.place !== null\n\n/*\n * Every language when the server sent them (`titles`), the one the list shows otherwise — filed\n * under the default language, which is where a place with one name has it.\n */\nfunction initial(): LocalizedValue {\n if (props.place === null) return {}\n if (props.place.titles) return { ...props.place.titles }\n\n const first = locales.list.value[0]?.code\n\n return first === undefined ? {} : { [first]: props.place.title }\n}\n\nconst key = ref(props.place?.key ?? '')\nconst title = ref<LocalizedValue>(initial())\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\nconst field = useTemplateRef<HTMLElement>('field')\n\nonMounted(async () => {\n await nextTick()\n field.value?.querySelector('input')?.focus()\n})\n\n/** Something written in at least one language: the server says which one it needs. */\nfunction written(): boolean {\n return Object.values(title.value).some((words) => words.trim() !== '')\n}\n\n/** The first line of a refusal under a field that may be named by language: `title.en`. */\nfunction errorOf(name: string): string | undefined {\n const found = Object.keys(errors.value).find((one) => one === name || one.startsWith(`${name}.`))\n\n return found === undefined ? undefined : errors.value[found]?.[0]\n}\n\nasync function submit(): Promise<void> {\n if (!written() || saving.value) return\n\n saving.value = true\n errors.value = {}\n\n try {\n resolve(\n editing && props.place !== null\n ? await api.renamePlace(props.place.key, title.value)\n : await api.createPlace({ key: key.value.trim(), title: title.value }),\n )\n } catch (error) {\n // Refused under its own field: a key that is taken or spelled in a way no template could\n // write, a name missing in the default language.\n errors.value = (error as { body?: { errors?: Record<string, string[]> } }).body?.errors ?? {}\n } finally {\n saving.value = false\n }\n}\n</script>\n\n<template>\n <wx-dialog\n v-model:open=\"open\"\n :title=\"editing ? t('places.rename-title') : t('places.new-title')\"\n :width=\"440\"\n >\n <wx-form-item :label=\"t('places.field-title')\" :error=\"errorOf('title')\" required>\n <div ref=\"field\">\n <wx-input\n v-model=\"title\"\n localized\n :aria-label=\"t('places.field-title')\"\n @keyup.enter=\"submit\"\n />\n </div>\n </wx-form-item>\n\n <wx-form-item\n :label=\"t('places.field-key')\"\n :error=\"errorOf('key')\"\n :help=\"editing ? undefined : t('places.key-help')\"\n required\n >\n <wx-input\n v-model=\"key\"\n :disabled=\"editing\"\n :aria-label=\"t('places.field-key')\"\n autocomplete=\"off\"\n @keyup.enter=\"submit\"\n />\n </wx-form-item>\n\n <template #footer>\n <wx-space size=\"sm\">\n <wx-button variant=\"outline\" @click=\"dismiss()\">{{ t('places.cancel') }}</wx-button>\n <wx-button\n type=\"primary\"\n :loading=\"saving\"\n :disabled=\"!written() || key.trim() === ''\"\n @click=\"submit\"\n >\n {{ editing ? t('places.save') : t('places.create') }}\n </wx-button>\n </wx-space>\n </template>\n </wx-dialog>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\nimport { useRoute, useRouter } from 'vue-router'\nimport {\n confirm,\n toast,\n WxAlert,\n WxBadge,\n WxEmpty,\n WxIcon,\n WxSegmented,\n WxSkeleton,\n WxSortableList,\n WxTooltip,\n type SegmentedValue,\n} from '@webx-ui/core'\nimport {\n useAdmin,\n useErrorText,\n useTranslate,\n WxRowMenu,\n WxScreenHead,\n type RowAction,\n type ScreenAction,\n} from '@webx-ui/module-admin'\nimport { createBannersApi } from './api'\nimport { useBannersMessages } from './i18n'\nimport type { BannerRow, PlaceRow } from './types'\n\n/**\n * The banners of one place: the right-hand half of the section, and the whole screen on a phone.\n *\n * The order here is the order on the site, and a place is small, so the list is the whole place\n * and a drag writes it whole (`reorder`). The bin is the same list turned over: nothing to drag,\n * and a banner in it opens nothing — it can only come back.\n *\n * The way back is drawn here and not by the pane around it: the drawer a narrow screen opens this\n * in has no close of its own on purpose (CLAUDE.md §4).\n */\ndefineOptions({ name: 'WxBannerPlacePane' })\n\nconst props = withDefaults(\n defineProps<{\n place: PlaceRow\n base: string\n /** False in the drawer a narrow screen opens this in. */\n inline?: boolean\n }>(),\n { inline: true },\n)\n\nconst emit = defineEmits<{\n back: []\n /** Something moved in or out of the place: the count beside its name is stale. */\n changed: []\n}>()\n\nconst admin = useAdmin()\nconst api = createBannersApi(admin)\nconst route = useRoute()\nconst router = useRouter()\nuseBannersMessages()\n\nconst t = useTranslate('webx-banners')\n/* Not the server's `message`: the panel says how a request failed in its own words. */\nconst message = useErrorText()\n\nconst rows = ref<BannerRow[]>([])\nconst loading = ref(true)\n\nconst canManage = computed(() => admin.can('banners.manage'))\n\n/* The bin is in the address with the place, so coming back from a banner lands where one was. */\nconst view = computed<SegmentedValue>({\n get: () => (route.query.view === 'trashed' ? 'trashed' : 'live'),\n set: (value) => {\n void router.replace({\n query: { ...route.query, view: value === 'trashed' ? 'trashed' : undefined },\n })\n },\n})\n\nconst inBin = computed(() => view.value === 'trashed')\n\nconst views = computed(() => [\n { value: 'live', label: t('banner.all') },\n { value: 'trashed', label: t('banner.trashed'), icon: 'trash' as const },\n])\n\nconst sortable = computed(() => canManage.value && !inBin.value && rows.value.length > 1)\n\nconst layout = computed(() => {\n const known = ['single', 'random', 'slider']\n\n return known.includes(props.place.layout) ? t(`places.layout-${props.place.layout}`) : ''\n})\n\nasync function load(): Promise<void> {\n loading.value = true\n\n try {\n rows.value = await api.list(props.place.key, inBin.value)\n } catch (error) {\n toast.danger(message(error))\n } finally {\n loading.value = false\n }\n}\n\nwatch(inBin, () => void load())\nvoid load()\n\nfunction edit(row: BannerRow): string {\n return `${props.base}/${row.id}`\n}\n\nfunction add(): void {\n void router.push({ path: `${props.base}/new`, query: { place: props.place.key } })\n}\n\n/**\n * The order on screen, written whole. The list is already in its new order (`WxSortableList`\n * reorders the model before it says so), so a failure has to put it back rather than leave the\n * screen disagreeing with the database.\n */\nasync function reorder(): Promise<void> {\n try {\n await api.reorder(\n props.place.key,\n rows.value.map((row) => row.id),\n )\n } catch (error) {\n toast.danger(message(error, t('banner.reorder-failed')))\n await load()\n }\n}\n\n/*\n * On and off from the list, through the form's own door: there is no switch of its own in the\n * API, and a second way to write a banner would be a second set of rules to keep in step. What\n * the form would refuse — a look the config dropped since — is refused here the same way.\n */\nasync function toggle(row: BannerRow): Promise<void> {\n try {\n const detail = await api.get(row.id)\n\n await api.save(row.id, { ...detail.values, enabled: !row.enabled })\n toast.success(row.enabled ? t('banner.switched-off') : t('banner.switched-on'))\n } catch (error) {\n toast.danger(message(error))\n } finally {\n await load()\n }\n}\n\nasync function remove(row: BannerRow): Promise<void> {\n const agreed = await confirm({\n title: t('banner.delete-title', { name: row.title }),\n message: t('banner.delete-text'),\n confirmText: t('banner.delete'),\n cancelText: t('banner.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.remove(row.id)\n toast.success(t('banner.deleted'))\n emit('changed')\n } catch (error) {\n toast.danger(message(error))\n } finally {\n await load()\n }\n}\n\nasync function restore(row: BannerRow): Promise<void> {\n try {\n await api.restore(row.id)\n toast.success(t('banner.restored'))\n emit('changed')\n } catch (error) {\n toast.danger(message(error))\n } finally {\n await load()\n }\n}\n\nfunction actionsFor(row: BannerRow): RowAction[] {\n if (inBin.value) {\n return canManage.value\n ? [\n {\n key: 'restore',\n icon: 'refresh',\n label: t('banner.restore'),\n run: () => void restore(row),\n },\n ]\n : []\n }\n\n const open: RowAction = {\n key: 'open',\n icon: 'edit',\n label: t('banner.open'),\n run: () => void router.push(edit(row)),\n }\n\n if (!canManage.value) return [open]\n\n return [\n open,\n {\n key: 'toggle',\n icon: row.enabled ? 'eye-off' : 'eye',\n label: row.enabled ? t('banner.disable') : t('banner.enable'),\n run: () => void toggle(row),\n },\n {\n key: 'delete',\n icon: 'trash',\n label: t('banner.delete'),\n danger: true,\n run: () => void remove(row),\n },\n ]\n}\n\n/* The one thing this pane exists for, in its head; on a narrow pane it stays a button. */\nconst actions = computed<ScreenAction[]>(() =>\n canManage.value && !inBin.value\n ? [{ key: 'new', label: t('banner.new'), icon: 'plus', primary: true, run: add }]\n : [],\n)\n</script>\n\n<template>\n <div class=\"wx-banner-place\">\n <wx-screen-head\n :level=\"3\"\n :back=\"!props.inline\"\n :back-label=\"t('places.places')\"\n :title=\"place.title\"\n :actions=\"actions\"\n :collapse-below=\"420\"\n @back=\"emit('back')\"\n >\n <template v-if=\"place.declared\" #title-after>\n <wx-tooltip :content=\"t('places.declared-help')\">\n <span class=\"wx-banner-place__lock\" tabindex=\"0\" :aria-label=\"t('places.declared')\">\n <wx-icon name=\"lock\" size=\"sm\" />\n </span>\n </wx-tooltip>\n </template>\n </wx-screen-head>\n\n <!--\n How a template asks for this place, where somebody writing one will look for it: the key\n is the whole contract between the panel and the site (decision 7).\n -->\n <p class=\"wx-banner-place__usage\">\n <span>{{ t('places.usage') }}</span>\n <code>banners('{{ place.key }}')</code>\n <span v-if=\"layout\" class=\"wx-banner-place__layout\">· {{ layout }}</span>\n </p>\n\n <wx-segmented\n v-model=\"view\"\n class=\"wx-banner-place__views\"\n size=\"sm\"\n :options=\"views\"\n :aria-label=\"t('banner.all')\"\n />\n\n <wx-skeleton v-if=\"loading && rows.length === 0\" :rows=\"4\" />\n\n <wx-empty\n v-else-if=\"rows.length === 0\"\n :title=\"inBin ? t('banner.empty-bin') : t('banner.empty')\"\n :description=\"inBin ? undefined : t('banner.empty-help')\"\n />\n\n <template v-else>\n <!-- A grip and not the whole row: the row opens a banner, and a row that both opens and\n drags is one where one of the two happens by accident. -->\n <wx-sortable-list\n v-model=\"rows\"\n class=\"wx-banner-place__list\"\n :class=\"{ 'is-loading': loading }\"\n plain\n item-key=\"id\"\n :handle=\"sortable ? 'grip' : 'row'\"\n :item-label=\"(item: BannerRow) => item.title\"\n :disabled=\"!sortable\"\n @move=\"reorder\"\n >\n <template #default=\"{ item }\">\n <component\n :is=\"inBin ? 'div' : 'router-link'\"\n :to=\"inBin ? undefined : edit(item)\"\n class=\"wx-banner-row\"\n :class=\"{ 'is-off': !item.enabled && !inBin }\"\n >\n <span class=\"wx-banner-row__thumb\">\n <img v-if=\"item.thumb\" :src=\"item.thumb\" alt=\"\" loading=\"lazy\" />\n <wx-icon v-else name=\"image\" size=\"sm\" :aria-label=\"t('banner.no-picture')\" />\n </span>\n\n <span class=\"wx-banner-row__body\">\n <span class=\"wx-banner-row__title\">{{ item.title }}</span>\n\n <span v-if=\"item.video || (!item.enabled && !inBin)\" class=\"wx-banner-row__facts\">\n <wx-badge v-if=\"item.video\" size=\"sm\" type=\"info\">{{ t('banner.video') }}</wx-badge>\n <wx-badge v-if=\"!item.enabled && !inBin\" dot size=\"sm\">{{\n t('banner.disabled')\n }}</wx-badge>\n </span>\n </span>\n </component>\n </template>\n\n <template #actions=\"{ item }\">\n <wx-row-menu :actions=\"actionsFor(item)\" :label=\"item.title\" />\n </template>\n </wx-sortable-list>\n\n <!-- What a drag does here, said once under the list. -->\n <wx-alert\n v-if=\"sortable\"\n class=\"wx-banner-place__note\"\n type=\"info\"\n :description=\"t('banner.order')\"\n />\n </template>\n </div>\n</template>\n\n<style scoped>\n.wx-banner-place {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-12);\n min-width: 0;\n padding: var(--wx-gap, var(--wx-space-16));\n}\n\n.wx-banner-place__lock {\n display: inline-flex;\n color: var(--wx-text-muted);\n}\n\n.wx-banner-place__usage {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--wx-space-6);\n margin: 0;\n color: var(--wx-text-muted);\n font-size: var(--wx-font-size-sm);\n}\n\n.wx-banner-place__usage code {\n padding: 0 var(--wx-space-4);\n border-radius: var(--wx-radius-xs);\n background: var(--wx-bg-subtle);\n color: var(--wx-text-default);\n font-family: var(--wx-font-family-mono);\n /* Copied whole, as somebody writing a template would: a click selects all of it. */\n user-select: all;\n}\n\n.wx-banner-place__views {\n align-self: flex-start;\n}\n\n.wx-banner-place__list.is-loading {\n opacity: 0.6;\n}\n\n.wx-banner-place__list :deep(.wx-sortable-list__row) {\n gap: var(--wx-space-4);\n padding-inline: var(--wx-space-4);\n border-radius: var(--wx-radius-sm);\n}\n\n.wx-banner-place__list :deep(.wx-sortable-list__row:hover) {\n background: var(--wx-bg-subtle);\n}\n\n.wx-banner-row {\n display: flex;\n align-items: center;\n gap: var(--wx-space-12);\n /* The row is the target: a short title should not leave half the line dead. */\n flex: 1 1 auto;\n width: 100%;\n min-width: 0;\n padding-block: var(--wx-space-8);\n color: inherit;\n text-decoration: none;\n}\n\n/* Off is not gone: still here, still in its place, just quieter than what is on the site. */\n.wx-banner-row.is-off .wx-banner-row__thumb,\n.wx-banner-row.is-off .wx-banner-row__title {\n opacity: 0.55;\n}\n\n.wx-banner-row__thumb {\n display: flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 80px;\n aspect-ratio: 16 / 9;\n overflow: hidden;\n border-radius: var(--wx-radius-xs);\n background: var(--wx-bg-fill);\n color: var(--wx-text-muted);\n}\n\n.wx-banner-row__thumb img {\n width: 100%;\n height: 100%;\n /* A thumbnail wider than its frame would otherwise hold the row open (CLAUDE.md §4). */\n min-width: 0;\n object-fit: cover;\n}\n\n.wx-banner-row__body {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n gap: var(--wx-space-4);\n min-width: 0;\n}\n\n.wx-banner-row__title {\n min-width: 0;\n overflow: hidden;\n font-weight: var(--wx-font-weight-medium);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\na.wx-banner-row:hover .wx-banner-row__title {\n color: var(--wx-color-primary);\n}\n\n.wx-banner-row__facts {\n display: flex;\n flex-wrap: wrap;\n gap: var(--wx-space-4);\n}\n\n.wx-banner-place__note {\n margin-top: var(--wx-space-4);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\nimport { useRoute, useRouter, type LocationQueryRaw } from 'vue-router'\nimport {\n confirm,\n createModal,\n toast,\n WxCard,\n WxEmpty,\n WxIcon,\n WxIndicator,\n WxListDetail,\n WxSkeleton,\n WxText,\n} from '@webx-ui/core'\nimport {\n useAdmin,\n useErrorText,\n useTranslate,\n WxListScreen,\n WxRowMenu,\n type RowAction,\n type ScreenAction,\n} from '@webx-ui/module-admin'\nimport { createBannersApi } from './api'\nimport { useBannersMessages } from './i18n'\nimport PlaceDialog from './PlaceDialog.vue'\nimport PlacePane from './PlacePane.vue'\nimport type { PlaceRow } from './types'\n\n/**\n * The section: the places of the site on the left, the banners of one of them on the right\n * (§5.4) — the shape of the menus, for the same reason: somebody who opens this is arranging one\n * place, not choosing between them, so the first one opens by itself where there is room for it.\n * On a phone it does not: there the same line would raise a panel over a list nobody touched.\n *\n * A declared place is on the list before anything was ever saved into it (decision 1) — its row\n * in the table is made by its first banner — so places are named by key throughout, not by id.\n */\nconst props = withDefaults(defineProps<{ base?: string }>(), { base: '/banners' })\n\nconst admin = useAdmin()\nconst api = createBannersApi(admin)\nconst route = useRoute()\nconst router = useRouter()\nuseBannersMessages()\n\nconst t = useTranslate('webx-banners')\n/* Not the server's `message`: the panel says how a request failed in its own words. */\nconst message = useErrorText()\n\nconst places = ref<PlaceRow[]>([])\nconst loading = ref(true)\nconst open = ref(false)\nconst detailInline = ref(true)\n\nconst canManage = computed(() => admin.can('banners.manage'))\n\nconst edit = createModal<PlaceRow, { place?: PlaceRow | null }>(PlaceDialog)\n\nconst title = computed(\n () =>\n admin.state.manifest?.modules.find((module) => module.id === 'banners')?.title ??\n t('module.banners'),\n)\n\n/** Which place is open, kept in the address — so \"back\" from a banner lands on its place. */\nconst current = computed<string | null>(() => {\n const asked = route.query.place\n\n return typeof asked === 'string' && asked !== '' ? asked : null\n})\n\nconst chosen = computed(() => places.value.find((place) => place.key === current.value) ?? null)\n\nasync function load(): Promise<void> {\n loading.value = true\n\n try {\n places.value = await api.places()\n } catch (error) {\n toast.danger(message(error))\n } finally {\n loading.value = false\n }\n}\n\nvoid load()\n\n/*\n * The first place, where there is a column to draw it in — only once the width is measured\n * (`detail-inline` says so), so a phone does not get a drawer over a list it never touched.\n */\nwatch([places, chosen, detailInline], () => {\n if (!detailInline.value || places.value.length === 0 || chosen.value !== null) return\n\n void router.replace({ query: { ...route.query, place: places.value[0]!.key } })\n})\n\nwatch(chosen, (place) => {\n if (place !== null && detailInline.value) open.value = true\n})\n\n/* Coming back from a banner on a phone lands on its place, and that is the drawer, open. */\nif (current.value !== null) open.value = true\n\nfunction choose(place: PlaceRow): void {\n const query: LocationQueryRaw = { ...route.query, place: place.key }\n\n // The bin of one place is not the bin of the next.\n delete query.view\n void router.replace({ query })\n open.value = true\n}\n\nasync function add(): Promise<void> {\n const made = await edit({ place: null })\n\n if (!made) return\n\n toast.success(t('places.created'))\n await load()\n choose(made)\n}\n\nasync function rename(place: PlaceRow): Promise<void> {\n const saved = await edit({ place })\n\n if (!saved) return\n\n toast.success(t('places.renamed'))\n await load()\n}\n\nasync function remove(place: PlaceRow): Promise<void> {\n const agreed = await confirm({\n title: t('places.delete-title', { place: place.title }),\n message: t('places.delete-text'),\n confirmText: t('places.delete'),\n cancelText: t('places.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.removePlace(place.key)\n toast.success(t('places.deleted'))\n\n if (current.value === place.key) {\n const rest = { ...route.query }\n\n delete rest.place\n delete rest.view\n void router.replace({ query: rest })\n open.value = false\n }\n } catch (error) {\n // The one refusal worth its own words: something is still in it — perhaps only in the bin,\n // which the count beside the name does not show (§5.6).\n toast.danger(message(error))\n } finally {\n await load()\n }\n}\n\nfunction actionsFor(place: PlaceRow): RowAction[] {\n if (!canManage.value || place.declared) return []\n\n return [\n { key: 'rename', icon: 'edit', label: t('places.rename'), run: () => void rename(place) },\n {\n key: 'delete',\n icon: 'trash',\n // Said in the line itself, since a switched-off line cannot carry a tooltip: an empty\n // place is the only kind that goes (decision 1).\n label: place.count > 0 ? t('places.delete-empty-first') : t('places.delete'),\n danger: true,\n disabled: place.count > 0,\n run: () => void remove(place),\n },\n ]\n}\n\nconst actions = computed<ScreenAction[]>(() =>\n canManage.value\n ? [{ key: 'new-place', label: t('places.new'), icon: 'plus', run: () => void add() }]\n : [],\n)\n</script>\n\n<template>\n <wx-list-screen :title=\"title\" :actions=\"actions\" :card=\"false\">\n <wx-card class=\"wx-banners\" padding=\"none\">\n <wx-list-detail\n v-model:open=\"open\"\n class=\"wx-banners__panes\"\n :list-width=\"300\"\n :detail-min=\"440\"\n :detail-label=\"t('places.places')\"\n @detail-inline=\"detailInline = $event\"\n >\n <template #list>\n <wx-skeleton\n v-if=\"loading && places.length === 0\"\n class=\"wx-banners__loading\"\n :rows=\"3\"\n />\n\n <wx-empty\n v-else-if=\"places.length === 0\"\n :title=\"t('places.empty')\"\n :description=\"t('places.empty-help')\"\n />\n\n <ul v-else class=\"wx-banners__list\">\n <li v-for=\"place in places\" :key=\"place.key\" class=\"wx-banners__row\">\n <button\n type=\"button\"\n class=\"wx-banners__open\"\n :class=\"{ 'is-current': place.key === current }\"\n @click=\"choose(place)\"\n >\n <span class=\"wx-banners__name\">\n <wx-text truncate weight=\"medium\">{{ place.title }}</wx-text>\n <wx-icon\n v-if=\"place.declared\"\n class=\"wx-banners__lock\"\n name=\"lock\"\n size=\"sm\"\n :aria-label=\"t('places.declared')\"\n />\n <wx-indicator\n class=\"wx-banners__count\"\n type=\"neutral\"\n :value=\"place.count\"\n :label=\"t('places.count')\"\n />\n </span>\n\n <wx-text size=\"sm\" tone=\"muted\" mono truncate>banners('{{ place.key }}')</wx-text>\n </button>\n\n <wx-row-menu\n v-if=\"actionsFor(place).length > 0\"\n :actions=\"actionsFor(place)\"\n :label=\"place.title\"\n />\n </li>\n </ul>\n </template>\n\n <template #detail=\"{ inline, back }\">\n <place-pane\n v-if=\"chosen\"\n :key=\"chosen.key\"\n :place=\"chosen\"\n :base=\"props.base\"\n :inline=\"inline\"\n @back=\"back\"\n @changed=\"load\"\n />\n </template>\n\n <template #empty>\n <wx-empty :title=\"t('places.choose')\" :description=\"t('places.choose-help')\" />\n </template>\n </wx-list-detail>\n </wx-card>\n </wx-list-screen>\n</template>\n\n<style>\n/*\n * What scrolls here is the page, the way it does on every other list of the panel. `clip`, not\n * `hidden`: `hidden` would make this a scroll container, and everything sticky inside would pin\n * itself to a box that never moves (CLAUDE.md §4).\n */\n.wx-banners > .wx-card__body {\n display: flex;\n flex-direction: column;\n overflow: clip;\n border-radius: inherit;\n}\n</style>\n\n<style scoped>\n.wx-banners__loading {\n padding: var(--wx-space-16);\n}\n\n.wx-banners__list {\n margin: 0;\n padding: var(--wx-space-8);\n list-style: none;\n}\n\n.wx-banners__row {\n display: flex;\n align-items: center;\n gap: var(--wx-space-4);\n margin: 0;\n padding-inline: var(--wx-space-8);\n border-radius: var(--wx-radius-sm);\n}\n\n.wx-banners__row:has(.is-current) {\n background: var(--wx-bg-subtle);\n}\n\n.wx-banners__open {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--wx-space-2);\n flex: 1 1 auto;\n min-width: 0;\n padding-block: var(--wx-space-8);\n padding-inline: 0;\n border: 0;\n background: none;\n font: inherit;\n color: inherit;\n text-align: start;\n cursor: pointer;\n}\n\n.wx-banners__open.is-current {\n color: var(--wx-color-primary);\n}\n\n.wx-banners__name {\n display: flex;\n align-items: center;\n gap: var(--wx-space-6);\n min-width: 0;\n max-width: 100%;\n}\n\n.wx-banners__lock {\n flex: none;\n color: var(--wx-text-muted);\n}\n\n.wx-banners__count {\n flex: 0 0 auto;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport {\n useLocales,\n WxButton,\n WxColorPicker,\n WxFormItem,\n WxIcon,\n WxInput,\n WxInputNumber,\n WxSegmented,\n WxSelect,\n WxSwitch,\n WxText,\n WxTextarea,\n type LocalizedValue,\n} from '@webx-ui/core'\nimport { useTranslate, WxLinkField, type LinkValue } from '@webx-ui/module-admin'\nimport { WxMediaField } from '@webx-ui/module-media'\nimport {\n clone,\n ENTRANCES,\n FRAME_LIMITS,\n LOOPS,\n ORIGINS,\n unknownFields,\n type BannerLayer,\n type LayerFrame,\n type LayerOrigin,\n type LayerStyle,\n type LayerView,\n type MediaRef,\n} from './layers'\n\n/**\n * What the selected layer is, in fields (§5.4 of the layers spec): its words, its geometry on the\n * layout being edited, its look and its motion. Every length is a percent of the picture's width.\n *\n * It never writes the layer itself: a changed layer goes up whole (`update`), a changed number of\n * the geometry as a patch of the layout on screen (`frame`) — the canvas decides which layout\n * that is, and copies the computer's to the phone on the phone's first change.\n */\ndefineOptions({ name: 'WxBannerLayerInspector' })\n\nconst props = defineProps<{\n layer: BannerLayer\n view: LayerView\n tags: { value: string; label: string }[]\n variants: { value: string; label: string }[]\n palette: { key: string; value: string }[]\n disabled?: boolean\n}>()\n\nconst emit = defineEmits<{\n update: [layer: BannerLayer]\n frame: [patch: Partial<LayerFrame>]\n 'same-as-desktop': []\n delete: []\n}>()\n\nconst t = useTranslate('webx-banners')\nconst locales = useLocales()\n\nconst current = computed<LayerFrame>(() =>\n props.view === 'mobile'\n ? (props.layer.frames.mobile ?? props.layer.frames.desktop)\n : props.layer.frames.desktop,\n)\n\nconst ownPhone = computed(() => props.view === 'mobile' && props.layer.frames.mobile !== null)\n\nfunction change(mutate: (layer: BannerLayer) => void): void {\n const next = clone(props.layer)\n\n mutate(next)\n emit('update', next)\n}\n\nfunction setStyle<K extends keyof LayerStyle>(key: K, value: LayerStyle[K]): void {\n change((layer) => {\n layer.style[key] = value\n })\n}\n\nconst hasWords = computed(() => props.layer.type === 'text' || props.layer.type === 'button')\n\nconst text = computed<LocalizedValue>({\n get: () => props.layer.text ?? {},\n set: (value) =>\n change((layer) => {\n layer.text = value\n }),\n})\n\nconst name = computed<string>({\n get: () => props.layer.name ?? '',\n set: (value) =>\n change((layer) => {\n layer.name = value\n }),\n})\n\n/** What is on screen in the active language — and which `{…}` in it is not a field. */\nconst typos = computed(() => unknownFields(text.value[locales.active.value] ?? ''))\nconst typoList = computed(() => typos.value.map((one) => `{${one}}`).join(', '))\n\n/**\n * The language whose words a reader of the active one sees when it is empty (decision 6), for\n * the field's hint.\n */\nconst fallsBackTo = computed(() => {\n const map = text.value\n const active = locales.active.value\n\n if ((map[active] ?? '').trim() !== '') return null\n\n return (\n [...locales.list.value.map((one) => one.code), ...Object.keys(map)].find(\n (code) => (map[code] ?? '').trim() !== '',\n ) ?? null\n )\n})\n\nfunction insert(field: 'title' | 'text'): void {\n const code = locales.active.value\n const words = text.value[code] ?? ''\n\n text.value = { ...text.value, [code]: `${words}${words === '' ? '' : ' '}{${field}}` }\n}\n\nconst image = computed<MediaRef | null>({\n get: () => props.layer.image ?? null,\n set: (value) =>\n change((layer) => {\n layer.image = value\n }),\n})\n\nconst link = computed<LinkValue | null>({\n get: () => props.layer.link ?? null,\n set: (value) =>\n change((layer) => {\n layer.link = value\n }),\n})\n\nconst choice = <T,>(get: () => T, set: (value: T) => void) =>\n computed<string | number | null>({\n get: () => get() as unknown as string | number | null,\n set: (value) => set(value as unknown as T),\n })\n\nconst tag = choice(\n () => props.layer.tag ?? 'p',\n (value: string) =>\n change((layer) => {\n layer.tag = value\n }),\n)\n\nconst variant = choice(\n () => props.layer.variant ?? props.variants[0]?.value ?? null,\n (value: string | null) =>\n change((layer) => {\n layer.variant = value\n }),\n)\n\nconst font = choice(\n () => props.layer.style.font ?? 'site',\n (value: string) => setStyle('font', value === 'heading' || value === 'body' ? value : null),\n)\n\nconst weight = choice(\n () => props.layer.style.weight,\n (value: number) => setStyle('weight', Number(value)),\n)\n\nconst align = computed<string | number>({\n get: () => props.layer.style.align,\n set: (value) => setStyle('align', value as LayerStyle['align']),\n})\n\nconst entrance = choice(\n () => props.layer.motion.in ?? 'none',\n (value: string) =>\n change((layer) => {\n layer.motion.in = (ENTRANCES as readonly string[]).includes(value)\n ? (value as BannerLayer['motion']['in'])\n : null\n }),\n)\n\nconst loop = choice(\n () => props.layer.motion.loop ?? 'none',\n (value: string) =>\n change((layer) => {\n layer.motion.loop = (LOOPS as readonly string[]).includes(value)\n ? (value as BannerLayer['motion']['loop'])\n : null\n }),\n)\n\ntype FrameNumber = keyof typeof FRAME_LIMITS\n\n/** The numbers of the geometry this kind of layer has. */\nconst numbers = computed<FrameNumber[]>(() => {\n switch (props.layer.type) {\n case 'image':\n return ['x', 'y', 'w', 'radius', 'rotate']\n case 'buttons':\n return ['x', 'y', 'w', 'size', 'gap', 'rotate']\n default:\n return ['x', 'y', 'w', 'size', 'pad', 'radius', 'tracking', 'rotate']\n }\n})\n\nconst steps: Record<FrameNumber, number> = {\n x: 0.05,\n y: 0.05,\n w: 0.5,\n size: 0.05,\n pad: 0.05,\n radius: 0.1,\n tracking: 0.01,\n rotate: 1,\n gap: 0.1,\n}\n\nfunction setNumber(key: FrameNumber, value: number | null): void {\n if (value === null || Number.isNaN(value)) return\n\n emit('frame', { [key]: value })\n}\n\nfunction setOrigin(origin: LayerOrigin): void {\n emit('frame', { origin })\n}\n\nconst fontOptions = computed(() => [\n { value: 'site', label: t('layers.font-site') },\n { value: 'heading', label: t('layers.font-heading') },\n { value: 'body', label: t('layers.font-body') },\n])\n\nconst weightOptions = [100, 200, 300, 400, 500, 600, 700, 800, 900].map((value) => ({\n value,\n label: String(value),\n}))\n\nconst alignOptions = computed(() => [\n { value: 'start', label: t('layers.align-start') },\n { value: 'center', label: t('layers.align-center') },\n { value: 'end', label: t('layers.align-end') },\n])\n\nconst entranceOptions = computed(() => [\n { value: 'none', label: t('layers.motion-none') },\n ...ENTRANCES.map((value) => ({ value, label: t(`layers.in-${value}`) })),\n])\n\nconst loopOptions = computed(() => [\n { value: 'none', label: t('layers.motion-none') },\n ...LOOPS.map((value) => ({ value, label: t(`layers.loop-${value}`) })),\n])\n\n/* A colour is a swatch of the theme's palette (it follows the theme) or one of its own. */\nfunction colorOf(key: 'color' | 'background'): string | null {\n const value = props.layer.style[key]\n\n if (value === null) return null\n if (value.startsWith('token:')) {\n return props.palette.find((one) => `token:${one.key}` === value)?.value ?? null\n }\n\n return value\n}\n\nfunction pickColor(key: 'color' | 'background', value: string | null): void {\n const hex =\n typeof value === 'string' && /^#[0-9a-f]{6}([0-9a-f]{2})?$/i.test(value)\n ? value.toLowerCase()\n : null\n\n setStyle(key, hex)\n}\n</script>\n\n<template>\n <div class=\"wx-banner-layer-inspector\">\n <div class=\"wx-banner-layer-inspector__head\">\n <wx-input\n v-model=\"name\"\n size=\"sm\"\n :placeholder=\"t('layers.name')\"\n :aria-label=\"t('layers.name')\"\n :disabled=\"disabled\"\n />\n <wx-button size=\"sm\" variant=\"outline\" danger :disabled=\"disabled\" @click=\"emit('delete')\">\n <template #icon><wx-icon name=\"trash\" /></template>\n {{ t('layers.delete') }}\n </wx-button>\n </div>\n\n <template v-if=\"hasWords\">\n <wx-form-item\n :label=\"layer.type === 'button' ? t('layers.label') : t('layers.words')\"\n :help=\"\n fallsBackTo\n ? t('layers.falls-back', { code: fallsBackTo.toUpperCase() })\n : t('layers.words-help')\n \"\n >\n <wx-textarea\n v-if=\"layer.type === 'text'\"\n v-model=\"text\"\n localized\n :rows=\"2\"\n :disabled=\"disabled\"\n />\n <wx-input v-else v-model=\"text\" localized :disabled=\"disabled\" />\n </wx-form-item>\n <div class=\"wx-banner-layer-inspector__fields\">\n <wx-button size=\"sm\" variant=\"text\" :disabled=\"disabled\" @click=\"insert('title')\"\n >+ {title}</wx-button\n >\n <wx-button size=\"sm\" variant=\"text\" :disabled=\"disabled\" @click=\"insert('text')\"\n >+ {text}</wx-button\n >\n </div>\n <wx-text v-if=\"typos.length > 0\" size=\"sm\" tone=\"warning\">\n {{ t('layers.unknown-field', { fields: typoList }) }}\n </wx-text>\n </template>\n\n <wx-form-item v-if=\"layer.type === 'text'\" :label=\"t('layers.tag')\">\n <wx-select v-model=\"tag\" :options=\"tags\" size=\"sm\" :disabled=\"disabled\" />\n </wx-form-item>\n\n <wx-form-item v-if=\"layer.type === 'image'\" :label=\"t('layers.image')\">\n <wx-media-field\n v-model=\"image\"\n accept=\"image\"\n :aspect=\"null\"\n :height=\"120\"\n :disabled=\"disabled\"\n />\n </wx-form-item>\n\n <template v-if=\"layer.type === 'button'\">\n <wx-form-item :label=\"t('layers.link')\">\n <wx-link-field v-model=\"link\" :allow-none=\"false\" :disabled=\"disabled\" />\n </wx-form-item>\n <wx-form-item :label=\"t('layers.variant')\">\n <wx-select v-model=\"variant\" :options=\"variants\" size=\"sm\" :disabled=\"disabled\" />\n </wx-form-item>\n </template>\n\n <wx-text v-if=\"layer.type === 'buttons'\" size=\"sm\" tone=\"muted\">{{\n t('layers.buttons-help')\n }}</wx-text>\n\n <section class=\"wx-banner-layer-inspector__group\">\n <div class=\"wx-banner-layer-inspector__title\">\n <wx-text size=\"sm\" strong>\n {{\n view === 'mobile'\n ? ownPhone\n ? t('layers.geometry-mobile-own')\n : t('layers.geometry-mobile-same')\n : t('layers.geometry-desktop')\n }}\n </wx-text>\n <wx-button\n v-if=\"ownPhone\"\n size=\"sm\"\n variant=\"text\"\n :disabled=\"disabled\"\n @click=\"emit('same-as-desktop')\"\n >\n {{ t('layers.same-as-desktop') }}\n </wx-button>\n </div>\n <wx-text size=\"sm\" tone=\"muted\">{{ t('layers.unit') }}</wx-text>\n\n <div class=\"wx-banner-layer-inspector__grid\">\n <wx-form-item v-for=\"key in numbers\" :key=\"key\" :label=\"t(`layers.${key}`)\">\n <wx-input-number\n :model-value=\"current[key]\"\n :min=\"FRAME_LIMITS[key][0]\"\n :max=\"FRAME_LIMITS[key][1]\"\n :step=\"steps[key]\"\n :precision=\"2\"\n size=\"sm\"\n :disabled=\"disabled\"\n @update:model-value=\"\n (value: number | null | undefined) => setNumber(key, value ?? null)\n \"\n />\n </wx-form-item>\n </div>\n\n <wx-form-item :label=\"t('layers.origin')\" :help=\"t('layers.origin-help')\">\n <div\n class=\"wx-banner-layer-inspector__origins\"\n role=\"radiogroup\"\n :aria-label=\"t('layers.origin')\"\n >\n <button\n v-for=\"origin in ORIGINS\"\n :key=\"origin\"\n type=\"button\"\n role=\"radio\"\n :aria-checked=\"current.origin === origin\"\n :aria-label=\"origin\"\n :disabled=\"disabled\"\n :class=\"[\n 'wx-banner-layer-inspector__origin',\n { 'is-active': current.origin === origin },\n ]\"\n @click=\"setOrigin(origin)\"\n />\n </div>\n </wx-form-item>\n\n <wx-switch\n :model-value=\"current.hidden\"\n :label=\"t('layers.hidden-here')\"\n :disabled=\"disabled\"\n @update:model-value=\"(value) => emit('frame', { hidden: value === true })\"\n />\n </section>\n\n <section v-if=\"layer.type !== 'image'\" class=\"wx-banner-layer-inspector__group\">\n <wx-text size=\"sm\" strong>{{ t('layers.look') }}</wx-text>\n\n <div class=\"wx-banner-layer-inspector__grid\">\n <wx-form-item :label=\"t('layers.font')\">\n <wx-select v-model=\"font\" :options=\"fontOptions\" size=\"sm\" :disabled=\"disabled\" />\n </wx-form-item>\n <wx-form-item :label=\"t('layers.weight')\">\n <wx-select v-model=\"weight\" :options=\"weightOptions\" size=\"sm\" :disabled=\"disabled\" />\n </wx-form-item>\n <wx-form-item :label=\"t('layers.line-height')\">\n <wx-input-number\n :model-value=\"layer.style.line_height\"\n :min=\"0.8\"\n :max=\"3\"\n :step=\"0.05\"\n :precision=\"2\"\n size=\"sm\"\n :disabled=\"disabled\"\n @update:model-value=\"\n (value: number | null | undefined) => value != null && setStyle('line_height', value)\n \"\n />\n </wx-form-item>\n <wx-form-item :label=\"t('layers.opacity')\">\n <wx-input-number\n :model-value=\"layer.style.opacity\"\n :min=\"0\"\n :max=\"1\"\n :step=\"0.05\"\n :precision=\"2\"\n size=\"sm\"\n :disabled=\"disabled\"\n @update:model-value=\"\n (value: number | null | undefined) => value != null && setStyle('opacity', value)\n \"\n />\n </wx-form-item>\n </div>\n\n <wx-form-item :label=\"t('layers.align')\">\n <wx-segmented v-model=\"align\" :options=\"alignOptions\" size=\"sm\" :disabled=\"disabled\" />\n </wx-form-item>\n\n <div class=\"wx-banner-layer-inspector__switches\">\n <wx-switch\n :model-value=\"layer.style.italic\"\n :label=\"t('layers.italic')\"\n :disabled=\"disabled\"\n @update:model-value=\"(value) => setStyle('italic', value === true)\"\n />\n <wx-switch\n :model-value=\"layer.style.uppercase\"\n :label=\"t('layers.uppercase')\"\n :disabled=\"disabled\"\n @update:model-value=\"(value) => setStyle('uppercase', value === true)\"\n />\n <wx-switch\n :model-value=\"layer.style.shadow\"\n :label=\"t('layers.shadow')\"\n :disabled=\"disabled\"\n @update:model-value=\"(value) => setStyle('shadow', value === true)\"\n />\n </div>\n\n <wx-form-item\n v-for=\"key in ['color', 'background'] as const\"\n :key=\"key\"\n :label=\"t(`layers.${key}`)\"\n >\n <div class=\"wx-banner-layer-inspector__color\">\n <button\n v-for=\"swatch in palette\"\n :key=\"swatch.key\"\n type=\"button\"\n :title=\"swatch.key\"\n :aria-label=\"swatch.key\"\n :disabled=\"disabled\"\n :class=\"[\n 'wx-banner-layer-inspector__swatch',\n { 'is-active': layer.style[key] === `token:${swatch.key}` },\n ]\"\n :style=\"{ background: swatch.value }\"\n @click=\"setStyle(key, `token:${swatch.key}`)\"\n />\n <wx-color-picker\n :model-value=\"colorOf(key)\"\n clearable\n size=\"sm\"\n :disabled=\"disabled\"\n @update:model-value=\"(value: string | null) => pickColor(key, value)\"\n />\n </div>\n </wx-form-item>\n </section>\n\n <section class=\"wx-banner-layer-inspector__group\">\n <wx-text size=\"sm\" strong>{{ t('layers.motion') }}</wx-text>\n <div class=\"wx-banner-layer-inspector__grid\">\n <wx-form-item :label=\"t('layers.entrance')\">\n <wx-select v-model=\"entrance\" :options=\"entranceOptions\" size=\"sm\" :disabled=\"disabled\" />\n </wx-form-item>\n <wx-form-item :label=\"t('layers.loop')\">\n <wx-select v-model=\"loop\" :options=\"loopOptions\" size=\"sm\" :disabled=\"disabled\" />\n </wx-form-item>\n <wx-form-item :label=\"t('layers.delay')\">\n <wx-input-number\n :model-value=\"layer.motion.delay\"\n :min=\"0\"\n :max=\"10000\"\n :step=\"50\"\n size=\"sm\"\n :disabled=\"disabled || layer.motion.in === null\"\n @update:model-value=\"\n (value: number | null | undefined) =>\n value != null && change((one) => (one.motion.delay = value))\n \"\n />\n </wx-form-item>\n <wx-form-item :label=\"t('layers.duration')\">\n <wx-input-number\n :model-value=\"layer.motion.duration\"\n :min=\"100\"\n :max=\"5000\"\n :step=\"50\"\n size=\"sm\"\n :disabled=\"disabled || layer.motion.in === null\"\n @update:model-value=\"\n (value: number | null | undefined) =>\n value != null && change((one) => (one.motion.duration = value))\n \"\n />\n </wx-form-item>\n </div>\n </section>\n </div>\n</template>\n\n<style scoped>\n.wx-banner-layer-inspector {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-12);\n min-width: 0;\n}\n\n.wx-banner-layer-inspector__head {\n display: flex;\n gap: var(--wx-space-8);\n}\n\n.wx-banner-layer-inspector__fields {\n display: flex;\n gap: var(--wx-space-4);\n margin-top: calc(-1 * var(--wx-space-8));\n}\n\n.wx-banner-layer-inspector__group {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-8);\n padding-top: var(--wx-space-12);\n border-top: 1px solid var(--wx-border-default);\n}\n\n.wx-banner-layer-inspector__title {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--wx-space-8);\n}\n\n.wx-banner-layer-inspector__grid {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 0 var(--wx-space-8);\n}\n\n.wx-banner-layer-inspector__grid :deep(.wx-form-item),\n.wx-banner-layer-inspector :deep(.wx-form-item) {\n margin-bottom: var(--wx-space-4);\n}\n\n.wx-banner-layer-inspector__switches {\n display: flex;\n flex-wrap: wrap;\n gap: var(--wx-space-12);\n}\n\n.wx-banner-layer-inspector__origins {\n display: grid;\n grid-template-columns: repeat(3, 18px);\n gap: var(--wx-space-4);\n}\n\n.wx-banner-layer-inspector__origin {\n width: 18px;\n height: 18px;\n padding: 0;\n border: 1px solid var(--wx-border-default);\n border-radius: var(--wx-radius-xs);\n background: var(--wx-bg-surface);\n cursor: pointer;\n}\n\n.wx-banner-layer-inspector__origin.is-active {\n border-color: var(--wx-color-primary);\n background: var(--wx-color-primary);\n}\n\n.wx-banner-layer-inspector__color {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--wx-space-4);\n}\n\n.wx-banner-layer-inspector__swatch {\n width: 22px;\n height: 22px;\n padding: 0;\n border: 1px solid var(--wx-border-default);\n border-radius: var(--wx-radius-full);\n cursor: pointer;\n}\n\n.wx-banner-layer-inspector__swatch.is-active {\n outline: 2px solid var(--wx-color-primary);\n outline-offset: 1px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'\nimport {\n useLocales,\n WxButton,\n WxIcon,\n WxSegmented,\n WxSortableList,\n WxText,\n WxTooltip,\n} from '@webx-ui/core'\nimport { useAdmin, useTranslate } from '@webx-ui/module-admin'\nimport { createMediaApi } from '@webx-ui/module-media'\nimport { createBannersApi } from './api'\nimport { useBannerEditor } from './editor'\nimport { useBannersMessages } from './i18n'\nimport LayerInspector from './LayerInspector.vue'\nimport {\n clampFrame,\n clone,\n cropBox,\n layerClasses,\n layerVars,\n layerWords,\n newLayer,\n ratioOf,\n type BannerLayer,\n type LayerFrame,\n type LayerType,\n type LayerView,\n} from './layers'\nimport type { BannerButton, BannersTheme } from './types'\nimport '../../../php/packages/module-banners/resources/css/layers.css'\n\n/**\n * The layers of a banner, edited on its picture (§5 of the layers spec).\n *\n * The canvas is the site: the same `layers.css`, the same custom properties the server prints,\n * every length a percent of the picture's width — so the width of this column changes the scale\n * and nothing else. It shows the whole picture and darkens what the frame of the place crops\n * away; a layer under the shade is not seen on the site.\n *\n * A field of `banners.form` like any other: the value is the list of layers, and every change is\n * a new list. Undo is this field's own — Ctrl+Z on the canvas — because a text input keeps its\n * own and the two must not fight.\n */\ndefineOptions({ name: 'WxBannerLayers' })\n\nconst props = defineProps<{\n modelValue?: BannerLayer[] | null\n disabled?: boolean\n}>()\n\nconst emit = defineEmits<{ 'update:modelValue': [value: BannerLayer[]] }>()\n\nconst admin = useAdmin()\nconst editor = useBannerEditor()\nconst locales = useLocales()\nconst media = createMediaApi(admin)\nconst api = createBannersApi(admin)\nuseBannersMessages()\n\nconst t = useTranslate('webx-banners')\n\nconst layers = computed<BannerLayer[]>(() => props.modelValue ?? [])\nconst place = computed(() => editor?.place.value?.layers ?? null)\n\nconst view = ref<LayerView>('desktop')\nconst selectedId = ref<string | null>(null)\nconst selected = computed(() => layers.value.find((layer) => layer.id === selectedId.value) ?? null)\n\nconst locale = computed(() => {\n const active = locales.active.value\n\n return locales.list.value.some((one) => one.code === active)\n ? active\n : (locales.list.value[0]?.code ?? 'en')\n})\nconst order = computed(() => locales.list.value.map((one) => one.code))\n\n/* --- History ---------------------------------------------------------------------------- */\n\nconst past = ref<string[]>([])\nconst future = ref<string[]>([])\n\nfunction remember(): void {\n past.value = [...past.value.slice(-99), JSON.stringify(layers.value)]\n future.value = []\n}\n\nfunction commit(next: BannerLayer[], record = true): void {\n if (record) remember()\n\n emit('update:modelValue', next)\n}\n\nfunction undo(): void {\n const last = past.value.at(-1)\n\n if (last === undefined) return\n\n future.value = [...future.value, JSON.stringify(layers.value)]\n past.value = past.value.slice(0, -1)\n emit('update:modelValue', JSON.parse(last) as BannerLayer[])\n}\n\nfunction redo(): void {\n const next = future.value.at(-1)\n\n if (next === undefined) return\n\n past.value = [...past.value, JSON.stringify(layers.value)]\n future.value = future.value.slice(0, -1)\n emit('update:modelValue', JSON.parse(next) as BannerLayer[])\n}\n\n/* --- Changing layers --------------------------------------------------------------------- */\n\nfunction replace(id: string, change: (layer: BannerLayer) => BannerLayer, record = true): void {\n commit(\n layers.value.map((layer) => (layer.id === id ? change(clone(layer)) : layer)),\n record,\n )\n}\n\n/** The layout being edited; the phone's is the computer's until it is changed (decision 3). */\nfunction frameOf(layer: BannerLayer, which: LayerView = view.value): LayerFrame {\n return which === 'mobile' ? (layer.frames.mobile ?? layer.frames.desktop) : layer.frames.desktop\n}\n\nfunction setFrame(id: string, patch: Partial<LayerFrame>, record = true): void {\n replace(\n id,\n (layer) => {\n const next = clampFrame({ ...frameOf(layer), ...patch })\n\n if (view.value === 'mobile') layer.frames.mobile = next\n else layer.frames.desktop = next\n\n return layer\n },\n record,\n )\n}\n\n/* Inspector edits typed in a row are one step of history, not one per key. */\nlet lastEdit = { id: '', at: 0 }\n\nfunction edit(next: BannerLayer): void {\n const now = Date.now()\n const merge = lastEdit.id === next.id && now - lastEdit.at < 800\n\n lastEdit = { id: next.id, at: now }\n replace(next.id, () => next, !merge)\n}\n\nfunction add(type: LayerType): void {\n const layer = newLayer(\n type,\n layers.value.map((one) => one.id),\n locale.value,\n )\n\n commit([layer, ...layers.value])\n selectedId.value = layer.id\n}\n\nfunction remove(id: string): void {\n commit(layers.value.filter((layer) => layer.id !== id))\n\n if (selectedId.value === id) selectedId.value = null\n}\n\nfunction duplicate(id: string): void {\n const index = layers.value.findIndex((layer) => layer.id === id)\n const source = layers.value[index]\n\n if (!source || source.type === 'buttons') return\n\n const copy = clone(source)\n\n copy.id = newLayer(\n 'text',\n layers.value.map((one) => one.id),\n locale.value,\n ).id\n copy.frames.desktop = {\n ...copy.frames.desktop,\n x: copy.frames.desktop.x + 2,\n y: copy.frames.desktop.y + 2,\n }\n\n const next = [...layers.value]\n\n next.splice(index, 0, copy)\n commit(next)\n selectedId.value = copy.id\n}\n\nfunction toggleHidden(layer: BannerLayer): void {\n setFrame(layer.id, { hidden: !frameOf(layer).hidden })\n}\n\nconst hasButtonsLayer = computed(() => layers.value.some((layer) => layer.type === 'buttons'))\n\nconst listModel = computed<BannerLayer[]>({\n get: () => layers.value,\n set: (next) => commit(next),\n})\n\n/* --- The pictures ------------------------------------------------------------------------ */\n\ninterface Picture {\n url: string\n ratio: number | null\n}\n\nconst pictures = reactive<Record<string, Picture | null>>({})\n\nfunction pathOf(value: unknown): string | null {\n const path = (value as { path?: unknown } | null)?.path\n\n return typeof path === 'string' && path !== '' ? path : null\n}\n\nasync function resolve(path: string | null): Promise<void> {\n if (path === null || path in pictures) return\n\n pictures[path] = null\n\n try {\n const file = await media.fileByPath(path)\n\n pictures[path] = file\n ? { url: file.url, ratio: file.width && file.height ? file.width / file.height : null }\n : null\n } catch {\n pictures[path] = null\n }\n}\n\nconst desktopPath = computed(() => pathOf(editor?.values.value.image))\nconst mobilePath = computed(() => pathOf(editor?.values.value.image_mobile))\n\nwatch(\n () => [desktopPath.value, mobilePath.value, ...layers.value.map((layer) => pathOf(layer.image))],\n (paths) => paths.forEach((path) => void resolve(path)),\n { immediate: true },\n)\n\nconst frameRatio = computed(() =>\n view.value === 'mobile'\n ? ratioOf(place.value?.ratio_mobile, 0.8)\n : ratioOf(place.value?.ratio, 16 / 6),\n)\n\nconst background = computed<Picture | null>(() => {\n const mobile = mobilePath.value === null ? null : (pictures[mobilePath.value] ?? null)\n const desktop = desktopPath.value === null ? null : (pictures[desktopPath.value] ?? null)\n\n return view.value === 'mobile' ? (mobile ?? desktop) : desktop\n})\n\n/* The canvas shows the whole picture: its own proportions, the frame's crop drawn over it. */\nconst imageRatio = computed(() => background.value?.ratio ?? frameRatio.value)\nconst crop = computed(() => cropBox(imageRatio.value, frameRatio.value))\n\nconst frameStyle = computed(() => {\n const ratio = String(imageRatio.value)\n\n return { '--ratio': ratio, '--ratio-mobile': ratio, '--img': ratio, '--img-m': ratio }\n})\n\n/* --- What a layer shows ------------------------------------------------------------------ */\n\n/* Exactly the language asked for: a field with no words in it puts in nothing, as on the site. */\nconst ownFields = computed(() => {\n const own = (field: 'title' | 'text'): string => {\n const value = editor?.values.value[field] as Record<string, string> | undefined\n\n return (value?.[locale.value] ?? '').trim()\n }\n\n return { title: own('title'), text: own('text') }\n})\n\nfunction wordsOf(layer: BannerLayer): string {\n return layerWords(layer.text, locale.value, order.value, ownFields.value)\n}\n\nconst bannerButtons = computed(() =>\n ((editor?.values.value.buttons as BannerButton[] | undefined) ?? [])\n .map((button) => ({\n label: (button.label?.[locale.value] ?? '').trim(),\n variant: button.variant ?? place.value?.variants[0]?.value ?? 'primary',\n }))\n .filter((button) => button.label !== ''),\n)\n\nfunction tagOf(layer: BannerLayer): string {\n return layer.type === 'text' && /^(p|span|h[1-6])$/.test(layer.tag ?? '')\n ? (layer.tag as string)\n : 'div'\n}\n\nfunction labelOf(layer: BannerLayer): string {\n if (layer.name) return layer.name\n\n const own = layer.text?.[locale.value] ?? Object.values(layer.text ?? {})[0] ?? ''\n\n if (own.trim() === '{title}') return t('layers.title-layer')\n if (own.trim() === '{text}') return t('layers.text-layer')\n if (layer.type === 'buttons') return t('layers.buttons-layer')\n if (layer.type === 'image') return t('layers.image')\n\n return own.trim() || t(`layers.${layer.type}`)\n}\n\nconst icons: Record<LayerType, string> = {\n text: 'file-text',\n image: 'image',\n button: 'link',\n buttons: 'grid',\n}\n\n/** A layer the frame hides on this layout: wholly under the shade. */\nfunction outside(layer: BannerLayer): boolean {\n const one = frameOf(layer)\n const height = 100 / imageRatio.value\n const box = crop.value\n const left = box.left\n const right = box.left + box.width\n const top = (box.top / 100) * height\n const bottom = ((box.top + box.height) / 100) * height\n\n // The box itself, not its anchor: a layer anchored on the right reaches left of its x.\n const x = one.x + (originShift(one.origin)[0] / 100) * one.w\n\n return x > right || x + one.w < left || one.y > bottom || one.y < top - 50\n}\n\n/* --- Pointer: move and resize ------------------------------------------------------------ */\n\nconst stage = ref<HTMLElement | null>(null)\nconst canvas = ref<HTMLElement | null>(null)\nconst guides = ref<{ axis: 'x' | 'y'; at: number }[]>([])\n\n/** How far the pointer must go before a press becomes a drag, in px. */\nconst SLOP = 3\n/** How close an edge must come to a guide to stick to it, in px. */\nconst SNAP = 6\n\nfunction snapTargets(axis: 'x' | 'y', skip: string): number[] {\n const height = 100 / imageRatio.value\n const box = crop.value\n const targets =\n axis === 'x'\n ? [0, 50, 100, box.left, box.left + box.width]\n : [0, height / 2, height, (box.top / 100) * height, ((box.top + box.height) / 100) * height]\n\n for (const layer of layers.value) {\n if (layer.id === skip) continue\n\n const one = frameOf(layer)\n\n if (axis === 'x') targets.push(one.x, one.x + one.w)\n else targets.push(one.y)\n }\n\n return targets\n}\n\nfunction snap(\n axis: 'x' | 'y',\n value: number,\n size: number,\n unit: number,\n skip: string,\n free: boolean,\n): number {\n if (free) return value\n\n const edges = [0, size / 2, size]\n let best: { delta: number; at: number } | null = null\n\n for (const target of snapTargets(axis, skip)) {\n for (const edge of edges) {\n const delta = target - (value + edge)\n\n if (\n Math.abs(delta) * unit <= SNAP &&\n (best === null || Math.abs(delta) < Math.abs(best.delta))\n ) {\n best = { delta, at: target }\n }\n }\n }\n\n if (best === null) return value\n\n guides.value = [...guides.value.filter((one) => one.axis !== axis), { axis, at: best.at }]\n\n return value + best.delta\n}\n\nfunction select(layer: BannerLayer): void {\n selectedId.value = layer.id\n canvas.value?.focus({ preventScroll: true })\n}\n\nfunction press(event: PointerEvent, layer: BannerLayer, mode: 'move' | 'width' | 'scale'): void {\n select(layer)\n\n if (props.disabled || event.button !== 0 || !stage.value) return\n\n event.preventDefault()\n\n const rect = stage.value.getBoundingClientRect()\n const unit = rect.width / 100\n const element = (event.target as HTMLElement).closest('.webx-banner__layer') as HTMLElement | null\n const box = element?.getBoundingClientRect()\n const height = box ? box.height / unit : 0\n const from = { ...frameOf(layer) }\n const start = { x: event.clientX, y: event.clientY }\n let moving = false\n\n const move = (next: PointerEvent): void => {\n const dx = (next.clientX - start.x) / unit\n const dy = (next.clientY - start.y) / unit\n\n if (!moving) {\n if (Math.hypot(next.clientX - start.x, next.clientY - start.y) < SLOP) return\n\n moving = true\n remember()\n }\n\n guides.value = []\n\n if (mode === 'move') {\n // The box's own left and top, so its edges and its middle are what sticks.\n const shift = {\n x: (originShift(from.origin)[0] / 100) * from.w,\n y: (originShift(from.origin)[1] / 100) * height,\n }\n const left = snap('x', from.x + dx + shift.x, from.w, unit, layer.id, next.altKey)\n const top = snap('y', from.y + dy + shift.y, height, unit, layer.id, next.altKey)\n\n setFrame(layer.id, { x: left - shift.x, y: top - shift.y }, false)\n } else if (mode === 'width') {\n setFrame(layer.id, { w: Math.max(1, from.w + dx) }, false)\n } else {\n const factor = Math.max(0.05, (from.w + dx) / from.w)\n\n setFrame(layer.id, { w: from.w * factor, size: from.size * factor }, false)\n }\n }\n\n const up = (): void => {\n window.removeEventListener('pointermove', move)\n window.removeEventListener('pointerup', up)\n window.removeEventListener('pointercancel', up)\n guides.value = []\n }\n\n window.addEventListener('pointermove', move)\n window.addEventListener('pointerup', up)\n window.addEventListener('pointercancel', up)\n}\n\nfunction originShift(origin: LayerFrame['origin']): [number, number] {\n const x = origin.endsWith('r')\n ? -100\n : origin === 't' || origin === 'c' || origin === 'b'\n ? -50\n : 0\n const y = origin.startsWith('b')\n ? -100\n : origin === 'l' || origin === 'c' || origin === 'r'\n ? -50\n : 0\n\n return [x, y]\n}\n\nfunction pressCanvas(event: PointerEvent): void {\n if (!(event.target as HTMLElement).closest('.webx-banner__layer')) selectedId.value = null\n}\n\n/* --- Keyboard ---------------------------------------------------------------------------- */\n\nfunction onKeydown(event: KeyboardEvent): void {\n const mod = event.ctrlKey || event.metaKey\n const key = event.key.toLowerCase()\n\n if (mod && key === 'z') {\n event.preventDefault()\n\n if (event.shiftKey) redo()\n else undo()\n\n return\n }\n\n if (mod && key === 'y') {\n event.preventDefault()\n redo()\n\n return\n }\n\n const layer = selected.value\n\n if (!layer || props.disabled) return\n\n if (mod && key === 'd') {\n event.preventDefault()\n duplicate(layer.id)\n\n return\n }\n\n if (key === 'delete' || key === 'backspace') {\n event.preventDefault()\n remove(layer.id)\n\n return\n }\n\n if (key === 'escape') {\n selectedId.value = null\n\n return\n }\n\n const step = event.shiftKey ? 0.5 : 0.05\n const arrows: Record<string, [number, number]> = {\n arrowleft: [-step, 0],\n arrowright: [step, 0],\n arrowup: [0, -step],\n arrowdown: [0, step],\n }\n const by = arrows[key]\n\n if (!by) return\n\n event.preventDefault()\n\n const one = frameOf(layer)\n const now = Date.now()\n // A key held down is one step of history.\n const merge = lastEdit.id === `arrows:${layer.id}` && now - lastEdit.at < 800\n\n lastEdit = { id: `arrows:${layer.id}`, at: now }\n setFrame(layer.id, { x: one.x + by[0], y: one.y + by[1] }, !merge)\n}\n\n/* --- The theme --------------------------------------------------------------------------- */\n\nconst theme = ref<BannersTheme | null>(null)\nlet themeStyle: HTMLStyleElement | null = null\n\nonMounted(async () => {\n try {\n theme.value = await api.theme()\n } catch {\n theme.value = null\n }\n\n const css = `${theme.value?.tokens_css ?? ''}\\n${theme.value?.fonts_css ?? ''}`.trim()\n\n if (css !== '' && !document.getElementById('wx-banners-theme')) {\n themeStyle = document.createElement('style')\n themeStyle.id = 'wx-banners-theme'\n themeStyle.textContent = css\n document.head.append(themeStyle)\n }\n})\n\nonBeforeUnmount(() => {\n themeStyle?.remove()\n})\n\nconst viewOptions = computed(() => [\n { value: 'desktop', label: t('layers.desktop'), icon: 'monitor' as const },\n { value: 'mobile', label: t('layers.mobile'), icon: 'smartphone' as const },\n])\n\nconst viewModel = computed<string | number>({\n get: () => view.value,\n set: (value) => {\n view.value = value === 'mobile' ? 'mobile' : 'desktop'\n },\n})\n</script>\n\n<template>\n <div class=\"wx-banner-layers\" :class=\"{ 'is-disabled': disabled }\">\n <div class=\"wx-banner-layers__bar\">\n <wx-segmented\n v-model=\"viewModel\"\n :options=\"viewOptions\"\n size=\"sm\"\n :aria-label=\"t('layers.layout')\"\n />\n\n <div class=\"wx-banner-layers__tools\">\n <wx-tooltip :content=\"t('layers.undo')\">\n <wx-button\n size=\"sm\"\n variant=\"text\"\n :disabled=\"disabled || past.length === 0\"\n :aria-label=\"t('layers.undo')\"\n @click=\"undo\"\n >\n <template #icon><wx-icon name=\"rotate-left\" /></template>\n </wx-button>\n </wx-tooltip>\n <wx-tooltip :content=\"t('layers.redo')\">\n <wx-button\n size=\"sm\"\n variant=\"text\"\n :disabled=\"disabled || future.length === 0\"\n :aria-label=\"t('layers.redo')\"\n @click=\"redo\"\n >\n <template #icon><wx-icon name=\"rotate-right\" /></template>\n </wx-button>\n </wx-tooltip>\n <wx-button size=\"sm\" variant=\"outline\" :disabled=\"disabled\" @click=\"add('text')\">\n <template #icon><wx-icon name=\"plus\" /></template>\n {{ t('layers.text') }}\n </wx-button>\n <wx-button size=\"sm\" variant=\"outline\" :disabled=\"disabled\" @click=\"add('image')\">\n <template #icon><wx-icon name=\"plus\" /></template>\n {{ t('layers.image') }}\n </wx-button>\n <wx-button size=\"sm\" variant=\"outline\" :disabled=\"disabled\" @click=\"add('button')\">\n <template #icon><wx-icon name=\"plus\" /></template>\n {{ t('layers.button') }}\n </wx-button>\n <wx-button\n v-if=\"!hasButtonsLayer\"\n size=\"sm\"\n variant=\"outline\"\n :disabled=\"disabled\"\n @click=\"add('buttons')\"\n >\n <template #icon><wx-icon name=\"plus\" /></template>\n {{ t('layers.buttons-layer') }}\n </wx-button>\n </div>\n </div>\n\n <wx-text v-if=\"!place\" size=\"sm\" tone=\"muted\">{{ t('layers.not-here') }}</wx-text>\n\n <div class=\"wx-banner-layers__body\">\n <div class=\"wx-banner-layers__main\">\n <div\n ref=\"canvas\"\n class=\"wx-banner-layers__canvas\"\n :class=\"{ 'is-overflow': place?.overflow }\"\n tabindex=\"0\"\n :aria-label=\"t('layers.canvas')\"\n @pointerdown=\"pressCanvas\"\n @keydown=\"onKeydown\"\n >\n <div\n class=\"webx-banner wx-banner-layers__frame\"\n :class=\"{ 'is-mobile': view === 'mobile' }\"\n :style=\"frameStyle\"\n >\n <div class=\"webx-banner__picture\">\n <img v-if=\"background\" class=\"webx-banner__image\" :src=\"background.url\" alt=\"\" />\n <div v-else class=\"wx-banner-layers__empty\">{{ t('layers.no-picture') }}</div>\n </div>\n\n <div ref=\"stage\" class=\"webx-banner__stage\">\n <component\n :is=\"tagOf(layer)\"\n v-for=\"(layer, index) in layers\"\n :key=\"layer.id\"\n :class=\"[\n ...layerClasses(layer),\n { 'is-selected': layer.id === selectedId, 'is-outside': outside(layer) },\n ]\"\n :style=\"layerVars(layer, layers.length - index)\"\n @pointerdown.stop=\"press($event, layer, 'move')\"\n >\n <span\n v-if=\"layer.type === 'text'\"\n class=\"webx-banner__inner wx-banner-layers__words\"\n >{{ wordsOf(layer) || '…' }}</span\n >\n <img\n v-else-if=\"layer.type === 'image'\"\n class=\"webx-banner__inner\"\n :src=\"pictures[pathOf(layer.image) ?? '']?.url ?? ''\"\n alt=\"\"\n draggable=\"false\"\n />\n <span\n v-else-if=\"layer.type === 'button'\"\n class=\"webx-banner__inner webx-banner__buttons\"\n >\n <span\n :class=\"[\n 'webx-banner__button',\n `webx-banner__button--${layer.variant ?? place?.variants[0]?.value ?? 'primary'}`,\n ]\"\n >\n {{ wordsOf(layer) || '…' }}\n </span>\n </span>\n <span v-else class=\"webx-banner__inner webx-banner__buttons\">\n <span\n v-for=\"(button, at) in bannerButtons\"\n :key=\"at\"\n :class=\"['webx-banner__button', `webx-banner__button--${button.variant}`]\"\n >{{ button.label }}</span\n >\n <span v-if=\"bannerButtons.length === 0\" class=\"webx-banner__button\">{{\n t('layers.buttons-layer')\n }}</span>\n </span>\n\n <template v-if=\"layer.id === selectedId && !disabled\">\n <span\n class=\"wx-banner-layers__handle wx-banner-layers__handle--width\"\n @pointerdown.stop=\"press($event, layer, 'width')\"\n />\n <span\n class=\"wx-banner-layers__handle wx-banner-layers__handle--scale\"\n @pointerdown.stop=\"press($event, layer, 'scale')\"\n />\n </template>\n </component>\n </div>\n\n <div\n class=\"wx-banner-layers__crop\"\n :style=\"{\n left: `${crop.left}%`,\n top: `${crop.top}%`,\n width: `${crop.width}%`,\n height: `${crop.height}%`,\n }\"\n />\n\n <div\n v-for=\"guide in guides\"\n :key=\"guide.axis\"\n :class=\"['wx-banner-layers__guide', `wx-banner-layers__guide--${guide.axis}`]\"\n :style=\"\n guide.axis === 'x' ? { left: `${guide.at}%` } : { top: `calc(${guide.at} * 1cqw)` }\n \"\n />\n </div>\n </div>\n\n <wx-text size=\"sm\" tone=\"muted\">{{ t('layers.hint') }}</wx-text>\n\n <wx-sortable-list\n v-model=\"listModel\"\n class=\"wx-banner-layers__list\"\n item-key=\"id\"\n size=\"sm\"\n :disabled=\"disabled\"\n :empty-text=\"t('layers.empty')\"\n :aria-label=\"t('layers.list')\"\n >\n <template #default=\"{ item }\">\n <button\n type=\"button\"\n class=\"wx-banner-layers__row\"\n :class=\"{ 'is-selected': (item as BannerLayer).id === selectedId }\"\n @click=\"select(item as BannerLayer)\"\n >\n <wx-icon :name=\"icons[(item as BannerLayer).type] as 'image'\" />\n <span class=\"wx-banner-layers__name\">{{ labelOf(item as BannerLayer) }}</span>\n <wx-icon\n v-if=\"outside(item as BannerLayer)\"\n name=\"warning\"\n class=\"wx-banner-layers__warn\"\n :aria-label=\"t('layers.outside')\"\n />\n </button>\n </template>\n <template #actions=\"{ item }\">\n <wx-button\n size=\"sm\"\n variant=\"text\"\n :disabled=\"disabled\"\n :aria-label=\"\n frameOf(item as BannerLayer).hidden ? t('layers.show') : t('layers.hide')\n \"\n @click=\"toggleHidden(item as BannerLayer)\"\n >\n <template #icon>\n <wx-icon :name=\"frameOf(item as BannerLayer).hidden ? 'eye-off' : 'eye'\" />\n </template>\n </wx-button>\n <wx-button\n size=\"sm\"\n variant=\"text\"\n :disabled=\"disabled\"\n :aria-label=\"t('layers.delete')\"\n @click=\"remove((item as BannerLayer).id)\"\n >\n <template #icon><wx-icon name=\"trash\" /></template>\n </wx-button>\n </template>\n </wx-sortable-list>\n </div>\n\n <layer-inspector\n v-if=\"selected\"\n class=\"wx-banner-layers__inspector\"\n :layer=\"selected\"\n :view=\"view\"\n :tags=\"place?.tags ?? []\"\n :variants=\"place?.variants ?? []\"\n :palette=\"theme?.palette ?? []\"\n :disabled=\"disabled\"\n @update=\"edit\"\n @frame=\"(patch: Partial<LayerFrame>) => setFrame(selected!.id, patch)\"\n @same-as-desktop=\"\n replace(selected!.id, (layer) => ({\n ...layer,\n frames: { ...layer.frames, mobile: null },\n }))\n \"\n @delete=\"remove(selected!.id)\"\n />\n <wx-text v-else class=\"wx-banner-layers__inspector\" size=\"sm\" tone=\"muted\">\n {{ t('layers.choose') }}\n </wx-text>\n </div>\n </div>\n</template>\n\n<style scoped>\n.wx-banner-layers {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-12);\n container-type: inline-size;\n min-width: 0;\n}\n\n.wx-banner-layers__bar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: var(--wx-space-8);\n}\n\n.wx-banner-layers__tools {\n display: flex;\n flex-wrap: wrap;\n gap: var(--wx-space-4);\n}\n\n.wx-banner-layers__body {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n gap: var(--wx-space-16);\n}\n\n@container (min-width: 900px) {\n .wx-banner-layers__body {\n grid-template-columns: minmax(0, 1fr) 320px;\n align-items: start;\n }\n}\n\n.wx-banner-layers__main {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-8);\n min-width: 0;\n}\n\n.wx-banner-layers__canvas {\n position: relative;\n /* The layers, the shade and the guides stack inside the canvas, never over the sticky bar. */\n isolation: isolate;\n overflow: hidden;\n border-radius: var(--wx-radius-sm);\n background: var(--wx-bg-muted, var(--wx-bg-surface));\n outline-offset: 2px;\n user-select: none;\n touch-action: none;\n}\n\n/* Room below the picture for a layer that steps out of the frame (decision 13). */\n.wx-banner-layers__canvas.is-overflow {\n padding-bottom: 24%;\n background-image: repeating-linear-gradient(\n 45deg,\n transparent 0 8px,\n color-mix(in srgb, var(--wx-text-muted) 12%, transparent) 8px 9px\n );\n}\n\n.wx-banner-layers__canvas:focus-visible {\n outline: 2px solid var(--wx-color-primary);\n}\n\n.wx-banner-layers__frame {\n overflow: visible;\n}\n\n.wx-banner-layers__frame :deep(.webx-banner__stage) {\n pointer-events: auto;\n}\n\n/* The site's motion is the site's: the canvas stands still, so a layer is where it ends up. */\n.wx-banner-layers__frame :deep(.webx-banner__layer),\n.wx-banner-layers__frame :deep(.webx-banner__inner) {\n animation: none !important;\n cursor: move;\n pointer-events: auto;\n}\n\n.wx-banner-layers__words {\n white-space: pre-line;\n}\n\n.wx-banner-layers__frame :deep(.webx-banner__layer.is-selected) {\n outline: 2px solid var(--wx-color-primary);\n outline-offset: 1px;\n}\n\n.wx-banner-layers__frame :deep(.webx-banner__layer:hover:not(.is-selected)) {\n outline: 1px dashed var(--wx-color-primary);\n}\n\n.wx-banner-layers__frame :deep(.webx-banner__layer.is-outside) {\n opacity: 0.5;\n}\n\n.wx-banner-layers__handle {\n position: absolute;\n width: 10px;\n height: 10px;\n border: 2px solid var(--wx-color-primary);\n border-radius: 2px;\n background: var(--wx-bg-surface);\n translate: 50% -50%;\n}\n\n.wx-banner-layers__handle--width {\n top: 50%;\n right: 0;\n cursor: ew-resize;\n}\n\n.wx-banner-layers__handle--scale {\n right: 0;\n bottom: 0;\n translate: 50% 50%;\n cursor: nwse-resize;\n}\n\n/* What the frame of the place crops away is shaded: the site never shows it. */\n.wx-banner-layers__crop {\n position: absolute;\n z-index: 1000;\n box-shadow: 0 0 0 100vmax rgb(0 0 0 / 0.45);\n outline: 1px dashed rgb(255 255 255 / 0.8);\n pointer-events: none;\n}\n\n.wx-banner-layers__guide {\n position: absolute;\n z-index: 1001;\n background: var(--wx-color-primary);\n pointer-events: none;\n}\n\n.wx-banner-layers__guide--x {\n top: 0;\n bottom: 0;\n width: 1px;\n}\n\n.wx-banner-layers__guide--y {\n left: 0;\n right: 0;\n height: 1px;\n}\n\n.wx-banner-layers__empty {\n display: grid;\n place-items: center;\n height: 100%;\n color: var(--wx-text-muted);\n}\n\n.wx-banner-layers__row {\n display: flex;\n align-items: center;\n gap: var(--wx-space-8);\n width: 100%;\n min-width: 0;\n padding: 0;\n border: 0;\n background: none;\n color: inherit;\n font: inherit;\n text-align: start;\n cursor: pointer;\n}\n\n.wx-banner-layers__row.is-selected {\n color: var(--wx-color-primary);\n font-weight: 600;\n}\n\n.wx-banner-layers__name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.wx-banner-layers__warn {\n color: var(--wx-color-warning);\n}\n</style>\n","import type { AdminModule } from '@webx-ui/module-admin'\nimport BannerEditorPage from './BannerEditorPage.vue'\nimport BannersPage from './BannersPage.vue'\nimport LayersField from './LayersField.vue'\n\nexport interface BannersOptions {\n /** Where the banners live inside the panel. */\n path?: string\n}\n\n/**\n * Banners as a section of the panel (§5.4): one module and one entry, without a group.\n *\n * Two screens. The places and the banners of one of them are a list and its detail, the way the\n * menus are; a banner is a page of its own, because its form is long — three pictures, the words\n * and up to three buttons — and a pane beside a list is no room for it. A section whose server\n * half is not installed never appears — the entry is built from the manifest.\n */\nexport function banners(options: BannersOptions = {}): AdminModule {\n const path = options.path ?? '/banners'\n\n return {\n id: 'banners',\n path,\n routes: [\n // The open place is in the address (`?place=`), so a link to it is a link to its banners.\n { path, name: 'webx.banners', component: BannersPage, props: { base: path } },\n // A banner not written yet: the place it goes to rides in the query (`?place=hero`).\n {\n path: `${path}/new`,\n name: 'webx.banners.new',\n component: BannerEditorPage,\n props: { base: path },\n },\n {\n path: `${path}/:id(\\\\d+)`,\n name: 'webx.banners.edit',\n component: BannerEditorPage,\n props: { base: path },\n },\n ],\n /*\n * The one node of `banners.form` only this module can draw: the layers, on the banner's\n * picture. A field like any other — its value is the list of layers.\n */\n types: {\n 'wx-banner-layers': { component: LayersField, kind: 'field', wide: true },\n },\n }\n}\n"],"names":["createBannersApi","admin","base","place","key","data","body","input","title","trashed","id","values","ids","provideBannerEditor","context","provide","useBannerEditor","inject","FRAME_LIMITS","ORIGINS","ORIGIN_SHIFT","ENTRANCES","LOOPS","FIELDS","frame","over","style","motion","clone","value","layerId","taken","used","newLayer","type","locale","defaultLayers","defaultLocale","next","layer","made","layerWords","text","order","fields","map","code","one","unknownFields","match","name","cssColor","color","token","round","layerVars","z","vars","desktop","mobile","prefix","ox","oy","background","layerClasses","classes","ratioOf","fallback","ratio","cropBox","image","frameRatio","width","height","clampFrame","min","max","bannersMessages","seeded","useBannersMessages","i18n","useAdmin","props","__props","api","route","useRoute","router","useRouter","locales","useLocales","t","useTranslate","message","useErrorText","computed","raw","banner","ref","snapshot","placeSaved","places","loading","saving","errors","canManage","current","dirty","state","written","localizedValue","back","section","module","placeRow","screenPatch","withDefaultLayers","model","placeOptions","placeModel","blank","take","detail","opened","load","list","asked","fresh","error","toast","save","remove","record","confirm","leaveGuard","event","onKeydown","useBodyKeys","watch","onMounted","onBeforeUnmount","onBeforeRouteLeave","actions","_createElementBlock","_Fragment","_createVNode","_unref","WxSkeleton","WxCard","WxScreenHead","WxBadge","WxFormItem","WxSelect","$event","WxScreen","_createBlock","WxActionBar","WxSaveState","WxButton","open","resolve","dismiss","useModal","editing","initial","first","field","useTemplateRef","nextTick","words","errorOf","found","submit","WxDialog","WxSpace","_createElementVNode","WxInput","emit","__emit","rows","view","inBin","views","sortable","layout","edit","row","add","reorder","toggle","restore","actionsFor","_openBlock","_hoisted_1","WxTooltip","WxIcon","_hoisted_3","_toDisplayString","_hoisted_4","WxSegmented","WxEmpty","WxSortableList","_normalizeClass","item","_withCtx","_resolveDynamicComponent","_hoisted_5","_hoisted_7","_hoisted_8","_hoisted_9","WxRowMenu","WxAlert","detailInline","createModal","PlaceDialog","chosen","choose","query","rename","rest","WxListScreen","WxListDetail","_cache","_renderList","WxText","_createTextVNode","WxIndicator","inline","PlacePane","ownPhone","change","mutate","setStyle","hasWords","typos","typoList","fallsBackTo","active","insert","link","choice","get","set","tag","variant","font","weight","align","entrance","loop","numbers","steps","setNumber","setOrigin","origin","fontOptions","weightOptions","alignOptions","entranceOptions","loopOptions","colorOf","pickColor","hex","_hoisted_2","WxTextarea","WxMediaField","WxLinkField","_hoisted_6","WxInputNumber","WxSwitch","_hoisted_10","_hoisted_11","_hoisted_12","swatch","_normalizeStyle","WxColorPicker","_hoisted_14","_hoisted_15","SLOP","SNAP","editor","media","createMediaApi","layers","selectedId","selected","past","future","remember","commit","undo","last","redo","replace","frameOf","which","setFrame","patch","lastEdit","now","merge","duplicate","index","source","copy","toggleHidden","hasButtonsLayer","listModel","pictures","reactive","pathOf","path","file","desktopPath","mobilePath","paths","imageRatio","crop","frameStyle","ownFields","own","wordsOf","bannerButtons","button","tagOf","labelOf","icons","outside","box","left","right","top","bottom","x","originShift","stage","canvas","guides","snapTargets","axis","skip","targets","snap","size","unit","free","edges","best","target","edge","delta","select","press","mode","from","start","moving","move","dx","dy","shift","factor","up","y","pressCanvas","mod","step","by","theme","themeStyle","css","viewOptions","viewModel","_withModifiers","at","_hoisted_13","guide","_hoisted_17","LayerInspector","banners","options","BannersPage","BannerEditorPage","LayersField"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/api.ts","../src/editor.ts","../src/layers.ts","../src/messages.ts","../src/i18n.ts","../src/BannerEditorPage.vue","../src/PlaceDialog.vue","../src/PlacePane.vue","../src/BannersPage.vue","../src/LayerInspector.vue","../src/LayersField.vue","../src/module.ts"],"sourcesContent":["import type { AdminContext } from '@webx-ui/module-admin'\nimport type { ScreenModel } from '@webx-ui/schema'\nimport type { BannerDetail, BannerRow, BannersTheme, PlaceInput, PlaceRow } from './types'\n\nexport interface BannersApi {\n /** Every place: the declared ones in the config's order, then the own ones by name. */\n places(): Promise<PlaceRow[]>\n createPlace(input: PlaceInput): Promise<PlaceRow>\n /** Only a place of somebody's own, and only its name: the key is what a template asks for. */\n renamePlace(key: string, title: PlaceInput['title']): Promise<PlaceRow>\n /** Only an own place with nothing in it, the bin included — a 422 with the count otherwise. */\n removePlace(key: string): Promise<void>\n\n /** The banners of a place in their order, or its bin. A declared place without a row: `[]`. */\n list(place: string, trashed?: boolean): Promise<BannerRow[]>\n get(id: number): Promise<BannerDetail>\n /**\n * A new banner out of the values of its form, in one request: the form is filled in first and\n * the record made on the first save, so nobody leaves an empty banner in a place.\n */\n create(place: string, values: ScreenModel): Promise<BannerDetail>\n /**\n * Refused with a 422 under the name of the field — `buttons.<n>.link` for a button's row. A\n * `place` other than the one it stands in moves it to the end of that one (decision 14).\n */\n save(id: number, values: ScreenModel, place?: string): Promise<BannerDetail>\n remove(id: number): Promise<void>\n restore(id: number): Promise<BannerRow>\n /** The order of one place, written whole. */\n reorder(place: string, ids: number[]): Promise<void>\n /** The site's theme as the layer editor shows it; empty on a site without one. */\n theme(): Promise<BannersTheme>\n}\n\n/** Everything under `/banners`, below the panel's API path (§5.6). */\nexport function createBannersApi(admin: AdminContext): BannersApi {\n const base = `${admin.apiPath}/banners`\n const place = (key: string) => `${base}/places/${encodeURIComponent(key)}`\n const data = <T>(body: { data: T }): T => body.data\n\n return {\n places: () => admin.http.get<{ data: PlaceRow[] }>(`${base}/places`).then(data),\n createPlace: (input) => admin.http.post<{ data: PlaceRow }>(`${base}/places`, input).then(data),\n renamePlace: (key, title) =>\n admin.http.put<{ data: PlaceRow }>(place(key), { title }).then(data),\n removePlace: (key) => admin.http.delete<void>(place(key)).then(() => undefined),\n\n list: (key, trashed = false) =>\n admin.http\n .get<{ data: BannerRow[] }>(`${place(key)}/banners${trashed ? '?trashed=1' : ''}`)\n .then(data),\n get: (id) => admin.http.get<{ data: BannerDetail }>(`${base}/${id}`).then(data),\n create: (key, values) =>\n admin.http.post<{ data: BannerDetail }>(`${place(key)}/banners`, { values }).then(data),\n save: (id, values, key) =>\n admin.http\n .put<{ data: BannerDetail }>(\n `${base}/${id}`,\n key === undefined ? { values } : { values, place: key },\n )\n .then(data),\n remove: (id) => admin.http.delete<void>(`${base}/${id}`).then(() => undefined),\n // The resource of the list line, and not the form's pair: a restored banner stays closed.\n restore: (id) => admin.http.post<{ data: BannerRow }>(`${base}/${id}/restore`, {}).then(data),\n reorder: (key, ids) =>\n admin.http.post<void>(`${place(key)}/reorder`, { ids }).then(() => undefined),\n theme: () => admin.http.get<{ data: BannersTheme }>(`${base}/theme`).then(data),\n }\n}\n","import { inject, provide, type ComputedRef, type InjectionKey, type Ref } from 'vue'\nimport type { ScreenModel } from '@webx-ui/schema'\nimport type { PlaceRow } from './types'\n\n/**\n * What the banner's page tells the fields of its screen that need more than their own value: the\n * layer editor draws the banner's picture, puts in its title and text, and lays out its buttons,\n * in the frame of the place the banner stands in.\n */\nexport interface BannerEditorContext {\n values: Ref<ScreenModel>\n place: ComputedRef<PlaceRow | null>\n}\n\nconst key: InjectionKey<BannerEditorContext> = Symbol('wx-banner-editor')\n\nexport function provideBannerEditor(context: BannerEditorContext): void {\n provide(key, context)\n}\n\nexport function useBannerEditor(): BannerEditorContext | null {\n return inject(key, null)\n}\n","import type { LocalizedValue } from '@webx-ui/core'\nimport type { LinkValue } from '@webx-ui/module-admin'\n\n/**\n * The layers of a banner on the panel's side (the layers spec, §3.2): their shape, the ones a\n * banner starts with, and the custom properties `layers.css` reads — the same numbers the server\n * prints (`Rendering\\Layers`), so the canvas is the site.\n */\n\nexport type LayerType = 'text' | 'image' | 'button' | 'buttons'\n\nexport type LayerOrigin = 'tl' | 't' | 'tr' | 'l' | 'c' | 'r' | 'bl' | 'b' | 'br'\n\nexport type LayerView = 'desktop' | 'mobile'\n\n/** One layout's geometry, every length in percent of the picture's width (decision 1). */\nexport interface LayerFrame {\n x: number\n y: number\n w: number\n size: number\n pad: number\n radius: number\n /** In em: letter spacing follows the type, not the picture. */\n tracking: number\n rotate: number\n gap: number\n origin: LayerOrigin\n hidden: boolean\n}\n\nexport interface LayerStyle {\n font: 'heading' | 'body' | null\n weight: number\n italic: boolean\n uppercase: boolean\n align: 'start' | 'center' | 'end'\n line_height: number\n /** `#rrggbb`, `token:<name of the palette>` or `null` — the site's. */\n color: string | null\n background: string | null\n opacity: number\n shadow: boolean\n}\n\nexport interface LayerMotion {\n in: 'fade' | 'rise' | 'slide-start' | 'slide-end' | 'zoom' | null\n loop: 'spin' | 'float' | null\n delay: number\n duration: number\n}\n\nexport interface MediaRef {\n path: string\n url?: string\n alt?: LocalizedValue | string\n}\n\nexport interface BannerLayer {\n id: string\n type: LayerType\n name?: string\n text?: LocalizedValue\n tag?: string\n image?: MediaRef | null\n link?: LinkValue | null\n variant?: string | null\n style: LayerStyle\n frames: { desktop: LayerFrame; mobile: LayerFrame | null }\n motion: LayerMotion\n}\n\n/** Range, step and default of each number of a layout — as `LayerRules::FRAME` has them. */\nexport const FRAME_LIMITS: Record<\n Exclude<keyof LayerFrame, 'origin' | 'hidden'>,\n [number, number, number]\n> = {\n x: [-50, 150, 5],\n y: [-50, 150, 5],\n w: [1, 150, 40],\n size: [0.3, 20, 3],\n pad: [0, 10, 0],\n radius: [0, 50, 0],\n tracking: [-0.2, 1, 0],\n rotate: [-180, 180, 0],\n gap: [0, 10, 1],\n}\n\nexport const ORIGINS: LayerOrigin[] = ['tl', 't', 'tr', 'l', 'c', 'r', 'bl', 'b', 'br']\n\nconst ORIGIN_SHIFT: Record<LayerOrigin, [number, number]> = {\n tl: [0, 0],\n t: [-50, 0],\n tr: [-100, 0],\n l: [0, -50],\n c: [-50, -50],\n r: [-100, -50],\n bl: [0, -100],\n b: [-50, -100],\n br: [-100, -100],\n}\n\nexport const ENTRANCES = ['fade', 'rise', 'slide-start', 'slide-end', 'zoom'] as const\n\nexport const LOOPS = ['spin', 'float'] as const\n\n/** The two fields of a banner a layer's words may put in. */\nexport const FIELDS = ['title', 'text'] as const\n\nexport function frame(over: Partial<LayerFrame> = {}): LayerFrame {\n return {\n x: 5,\n y: 5,\n w: 40,\n size: 3,\n pad: 0,\n radius: 0,\n tracking: 0,\n rotate: 0,\n gap: 1,\n origin: 'tl',\n hidden: false,\n ...over,\n }\n}\n\nexport function style(over: Partial<LayerStyle> = {}): LayerStyle {\n return {\n font: null,\n weight: 400,\n italic: false,\n uppercase: false,\n align: 'start',\n line_height: 1.2,\n color: null,\n background: null,\n opacity: 1,\n shadow: false,\n ...over,\n }\n}\n\nexport function motion(over: Partial<LayerMotion> = {}): LayerMotion {\n return { in: null, loop: null, delay: 0, duration: 600, ...over }\n}\n\n/**\n * A deep copy of a layer. Not `structuredClone`: what the form holds is a reactive proxy, and the\n * browser refuses to clone one.\n */\nexport function clone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T\n}\n\n/** A short id, unique among the layers it joins. */\nexport function layerId(taken: Iterable<string> = []): string {\n const used = new Set(taken)\n let id = ''\n\n do {\n id = Math.random().toString(36).slice(2, 8).padEnd(6, '0')\n } while (used.has(id) || !/^[a-z0-9]{4,12}$/.test(id))\n\n return id\n}\n\n/** A new layer of a kind, where a new layer of that kind usually goes. */\nexport function newLayer(type: LayerType, taken: Iterable<string>, locale: string): BannerLayer {\n const base = { id: layerId(taken), type, style: style(), motion: motion() }\n\n switch (type) {\n case 'text':\n return {\n ...base,\n text: { [locale]: 'Text' },\n tag: 'p',\n frames: { desktop: frame({ x: 10, y: 10, size: 2.4 }), mobile: null },\n }\n case 'image':\n return {\n ...base,\n image: null,\n frames: { desktop: frame({ x: 60, y: 10, w: 20 }), mobile: null },\n }\n case 'button':\n return {\n ...base,\n text: { [locale]: 'Button' },\n link: null,\n variant: null,\n frames: { desktop: frame({ x: 10, y: 25, w: 30, size: 1.2 }), mobile: null },\n }\n case 'buttons':\n return {\n ...base,\n frames: { desktop: frame({ x: 6, y: 27, w: 50, size: 1.2 }), mobile: null },\n }\n }\n}\n\n/**\n * What a banner of a place with layers starts with (decision 5): its title, its text and its\n * buttons, in a column on the left. The words are written once, in the default language — the\n * others take them from there (decision 6) — and the fields are put in in the page's language.\n */\nexport function defaultLayers(defaultLocale: string): BannerLayer[] {\n const taken: string[] = []\n const next = (layer: Omit<BannerLayer, 'id'>): BannerLayer => {\n const made = { ...layer, id: layerId(taken) } as BannerLayer\n\n taken.push(made.id)\n\n return made\n }\n\n return [\n next({\n type: 'text',\n text: { [defaultLocale]: '{title}' },\n tag: 'h2',\n style: style({ weight: 700, line_height: 1.05, color: '#ffffff', shadow: true }),\n frames: { desktop: frame({ x: 6, y: 6, w: 45, size: 4.2 }), mobile: null },\n motion: motion({ in: 'rise' }),\n }),\n next({\n type: 'text',\n text: { [defaultLocale]: '{text}' },\n tag: 'p',\n style: style({ line_height: 1.4, color: '#ffffff', shadow: true }),\n frames: { desktop: frame({ x: 6, y: 18, w: 40, size: 1.4 }), mobile: null },\n motion: motion({ in: 'rise', delay: 150 }),\n }),\n next({\n type: 'buttons',\n style: style(),\n frames: { desktop: frame({ x: 6, y: 28, w: 50, size: 1.2 }), mobile: null },\n motion: motion({ in: 'rise', delay: 300 }),\n }),\n ]\n}\n\n/**\n * A layer's words in this language: the page's, else the site's default, else the first one\n * written in the order of the site's languages (decision 6) — then the banner's fields put in,\n * in the page's language. The same as `Rendering\\Layers::words()`.\n */\nexport function layerWords(\n text: LocalizedValue | undefined,\n locale: string,\n order: string[],\n fields: Partial<Record<(typeof FIELDS)[number], string>>,\n): string {\n const map = text ?? {}\n const codes = [...new Set([locale, ...order, ...Object.keys(map)])]\n const words = codes.map((code) => map[code]).find((one) => (one ?? '').trim() !== '') ?? ''\n\n return words\n .replaceAll('{title}', fields.title ?? '')\n .replaceAll('{text}', fields.text ?? '')\n .trim()\n}\n\n/** `{title}` and `{text}` are fields; any other `{…}` in a layer's words is very likely a typo. */\nexport function unknownFields(text: string): string[] {\n return [...text.matchAll(/\\{([a-z_.]+)\\}/g)]\n .map((match) => match[1] ?? '')\n .filter((name) => !(FIELDS as readonly string[]).includes(name))\n}\n\n/** A colour of the theme's palette, or one of its own; anything else is no colour at all. */\nexport function cssColor(color: string | null | undefined): string | null {\n if (typeof color !== 'string') return null\n if (/^#[0-9a-f]{6}([0-9a-f]{2})?$/.test(color)) return color\n\n const token = /^token:([a-z][a-z0-9-]{0,40})$/.exec(color)\n\n return token ? `var(--site-color-${token[1]})` : null\n}\n\nconst round = (value: number): number => Math.round(value * 100) / 100\n\n/** The custom properties of a layer, as the server prints them, for both layouts. */\nexport function layerVars(layer: BannerLayer, z: number): Record<string, string> {\n const vars: Record<string, string> = {}\n const desktop = layer.frames.desktop\n const mobile = layer.frames.mobile ?? desktop\n\n for (const [prefix, one] of [\n ['', desktop],\n ['m-', mobile],\n ] as const) {\n for (const key of Object.keys(FRAME_LIMITS) as (keyof typeof FRAME_LIMITS)[]) {\n vars[`--${prefix}${key}`] = String(round(one[key]))\n }\n\n const [ox, oy] = ORIGIN_SHIFT[one.origin] ?? [0, 0]\n\n vars[`--${prefix}ox`] = `${ox}%`\n vars[`--${prefix}oy`] = `${oy}%`\n vars[`--${prefix}display`] = one.hidden ? 'none' : 'block'\n }\n\n vars['--weight'] = String(layer.style.weight)\n vars['--leading'] = String(layer.style.line_height)\n vars['--opacity'] = String(layer.style.opacity)\n vars['--align'] = layer.style.align\n\n const color = cssColor(layer.style.color)\n const background = cssColor(layer.style.background)\n\n if (color) vars['--color'] = color\n if (background) vars['--background'] = background\n if (layer.style.font) vars['--font'] = `var(--site-font-${layer.style.font})`\n\n vars['z-index'] = String(z)\n\n return vars\n}\n\n/** The classes of a layer's element, as the server prints them (motion is the site's, not the canvas's). */\nexport function layerClasses(layer: BannerLayer): string[] {\n const classes = ['webx-banner__layer', `webx-banner__layer--${layer.type}`]\n\n if (layer.style.italic) classes.push('is-italic')\n if (layer.style.uppercase) classes.push('is-uppercase')\n if (layer.style.shadow) classes.push('is-shadow')\n if (layer.style.background) classes.push('has-background')\n\n return classes\n}\n\n/** `16/6`, `16 / 6`, `2.4` → width over height; anything else is `fallback`. */\nexport function ratioOf(value: string | number | null | undefined, fallback: number): number {\n if (typeof value === 'number') return value > 0 ? value : fallback\n\n const match = /^\\s*(\\d+(?:\\.\\d+)?)\\s*(?:\\/\\s*(\\d+(?:\\.\\d+)?))?\\s*$/.exec(value ?? '')\n\n if (!match) return fallback\n\n const ratio = Number(match[1]) / Number(match[2] ?? 1)\n\n return Number.isFinite(ratio) && ratio > 0 ? ratio : fallback\n}\n\n/**\n * Where the frame of the place falls on the picture — what the site shows of it — as percent of\n * the picture's width and height. `cover` crops the longer side, centred.\n */\nexport function cropBox(\n image: number,\n frameRatio: number,\n): { left: number; top: number; width: number; height: number } {\n if (image > frameRatio) {\n const width = (frameRatio / image) * 100\n\n return { left: (100 - width) / 2, top: 0, width, height: 100 }\n }\n\n const height = (image / frameRatio) * 100\n\n return { left: 0, top: (100 - height) / 2, width: 100, height }\n}\n\n/** A layer as it is stored: the phone's layout, when it differs, or `null`. */\nexport function clampFrame(one: LayerFrame): LayerFrame {\n const next = { ...one }\n\n for (const key of Object.keys(FRAME_LIMITS) as (keyof typeof FRAME_LIMITS)[]) {\n const [min, max] = FRAME_LIMITS[key]\n\n next[key] = round(Math.min(max, Math.max(min, Number(next[key]) || 0)))\n }\n\n return next\n}\n","import type { Messages } from '@webx-ui/module-admin'\n\n/**\n * The English this package speaks on its own, before the server's dictionary arrives, or without\n * one. The server ships the same lines in ten languages under `webx-banners::` and overrides\n * these; `messages.test.ts` keeps the two sets of keys equal. The server's refusals (`errors`)\n * are not here: the panel only ever shows them as they come.\n */\nexport const bannersMessages: Record<string, Messages> = {\n module: {\n banners: 'Banners',\n },\n places: {\n hero: 'Home page slider',\n promo: 'Promo strip',\n notice: 'Announcement bar',\n list: 'Places',\n new: 'New place',\n title: 'Name',\n key: 'Key',\n 'key-help':\n 'What the site’s template asks for: lowercase Latin letters, digits and dashes, starting with a letter. It cannot be changed later.',\n declared: 'Declared in the configuration',\n 'in-template': \"In a template: banners(':key')\",\n count: 'Banners',\n rename: 'Rename',\n delete: 'Delete',\n 'delete-confirm': 'Delete the place “:title”?',\n 'delete-not-empty': 'Only an empty place can be deleted — the bin counts too.',\n empty: 'No places yet',\n choose: 'Choose a place on the left',\n create: 'Create',\n save: 'Save',\n cancel: 'Cancel',\n created: 'Place created',\n renamed: 'Place renamed',\n deleted: 'Place deleted',\n places: 'Places',\n 'new-title': 'New place',\n 'rename-title': 'Rename the place',\n 'delete-title': 'Delete the place “:place”?',\n 'delete-text':\n 'There is nothing in it. A template that still asks for it gets an empty list, not an error.',\n 'delete-empty-first': 'Delete: empty it first',\n 'delete-blocked': 'Banners in this place, the bin included: :count. Take them out first.',\n 'field-title': 'Name',\n 'field-key': 'Key',\n 'declared-help': 'Cannot be renamed or deleted here: a template asks for it by its key.',\n usage: 'In a template:',\n 'layout-single': 'One banner',\n 'layout-random': 'A random one',\n 'layout-slider': 'Slider',\n 'empty-help': 'The site’s config declares the places its templates ask for.',\n 'choose-help': 'Its banners open beside the list.',\n },\n banner: {\n new: 'New banner',\n trash: 'Bin',\n 'to-trash': 'Move to the bin',\n restore: 'Restore',\n open: 'Open',\n enable: 'Turn on',\n disable: 'Turn off',\n disabled: 'Off',\n video: 'Video',\n empty: 'No banners in this place yet',\n 'trash-empty': 'The bin is empty',\n place: 'Place',\n 'place-help': 'Moving the banner puts it at the end of the new place.',\n back: 'Back to the place',\n save: 'Save',\n saved: 'Banner saved',\n trashed: 'Bin',\n restored: 'Banner restored',\n 'leave-title': 'Leave without saving?',\n 'leave-text': 'The changes to this banner will be lost.',\n leave: 'Leave',\n stay: 'Stay',\n reorder: 'Reorder',\n untitled: 'Untitled banner',\n all: 'Banners',\n 'empty-help': 'The site shows them wherever a template asks for this place.',\n 'empty-bin': 'The bin is empty.',\n 'no-picture': 'No picture',\n order: 'Drag to change the order on the site.',\n 'switched-on': 'Switched on.',\n 'switched-off': 'Switched off.',\n delete: 'Delete',\n 'delete-title': 'Delete “:name”?',\n 'delete-text': 'It goes to the bin and leaves the site. Restored, it comes back to its place.',\n deleted: 'Moved to the bin.',\n 'reorder-failed': 'The new order was not saved.',\n 'save-failed': 'Not saved.',\n cancel: 'Cancel',\n previous: 'Previous banner',\n next: 'Next banner',\n },\n screen: {\n media: 'Pictures and video',\n image: 'Picture',\n 'image-help': 'Required. Shown on wide screens, and as the poster of the video.',\n 'image-mobile': 'Picture for phones',\n 'image-mobile-help': 'Optional. Narrow screens show it instead of the picture above.',\n video: 'Video',\n 'video-help':\n 'Optional. Plays silently over the picture; the picture becomes its poster and what is seen without it.',\n words: 'Words',\n title: 'Title',\n 'title-help':\n 'A banner with words is shown only in the languages they are written in; one without words is shown everywhere.',\n text: 'Text',\n 'text-help': 'Plain text; line breaks are kept.',\n buttons: 'Buttons',\n 'buttons-help': 'Up to three. A button without a label in a language is not shown in it.',\n 'buttons-add': 'Add a button',\n 'buttons-empty': 'No buttons',\n label: 'Label',\n link: 'Link',\n variant: 'Look',\n 'variant-placeholder': 'The first one',\n settings: 'Settings',\n enabled: 'On',\n 'enabled-help': 'Only a banner that is on is shown on the site. A new one starts off.',\n layers: 'Layers',\n 'layers-help':\n 'Texts, pictures and buttons placed on the picture. Every size is a percent of the picture’s width, so the banner scales as a whole.',\n },\n layers: {\n text: 'Text',\n image: 'Picture',\n button: 'Button',\n 'title-layer': 'Banner title',\n 'text-layer': 'Banner text',\n 'buttons-layer': 'Banner buttons',\n desktop: 'Computer',\n mobile: 'Phone',\n layout: 'Layout',\n undo: 'Undo (Ctrl+Z)',\n redo: 'Redo (Ctrl+Shift+Z)',\n 'not-here': 'This place does not print layers: they are kept, but the site does not show them.',\n canvas: 'Banner canvas',\n 'no-picture': 'Choose a picture above',\n hint: 'Click to select, drag to move, the handles resize. Arrows move by 0.05 (with Shift by 0.5), Delete removes, Ctrl+D duplicates, Ctrl+Z undoes. Alt while dragging: no snapping. The shade is what the frame of the place crops away.',\n empty: 'No layers yet',\n list: 'Layers',\n outside: 'Outside the frame: not seen on the site',\n show: 'Show on this layout',\n hide: 'Hide on this layout',\n delete: 'Delete',\n choose: 'Select a layer on the picture or in the list.',\n name: 'Name in the list',\n label: 'Label',\n words: 'Words',\n 'falls-back': 'Empty here: the site shows the words in :code.',\n 'words-help':\n 'Empty in a language — the default language is shown. {title} and {text} put in the banner’s fields.',\n 'unknown-field': 'Not a field of the banner: :fields. Fields are {title} and {text}.',\n tag: 'Tag',\n link: 'Link',\n variant: 'Look',\n 'buttons-help': 'The buttons of the banner, in a row. Edit them in “Buttons” below.',\n 'geometry-desktop': 'Position on a computer',\n 'geometry-mobile-own': 'Position on a phone',\n 'geometry-mobile-same': 'Position on a phone — as on a computer',\n 'same-as-desktop': 'As on a computer',\n unit: 'In percent of the picture’s width.',\n x: 'x (from the left)',\n y: 'y (from the top)',\n w: 'Width',\n size: 'Font size',\n pad: 'Padding',\n radius: 'Rounding',\n tracking: 'Letter spacing, em',\n rotate: 'Rotation, °',\n gap: 'Gap',\n origin: 'Anchor',\n 'origin-help': 'Which point of the layer stands at x and y: longer words grow away from it.',\n 'hidden-here': 'Hidden on this layout',\n look: 'Look',\n font: 'Font',\n 'font-site': 'As on the site',\n 'font-heading': 'Headings',\n 'font-body': 'Body text',\n weight: 'Weight',\n 'line-height': 'Line height',\n opacity: 'Opacity',\n align: 'Alignment',\n 'align-start': 'Start',\n 'align-center': 'Centre',\n 'align-end': 'End',\n italic: 'Italic',\n uppercase: 'Capitals',\n shadow: 'Shadow',\n color: 'Colour',\n background: 'Background',\n motion: 'Motion',\n entrance: 'Entrance',\n loop: 'Loop',\n delay: 'Delay, ms',\n duration: 'Duration, ms',\n 'motion-none': 'None',\n 'in-fade': 'Fade in',\n 'in-rise': 'Rise',\n 'in-slide-start': 'From the start',\n 'in-slide-end': 'From the end',\n 'in-zoom': 'Zoom',\n 'loop-spin': 'Spin',\n 'loop-float': 'Float',\n },\n}\n","import { useAdmin } from '@webx-ui/module-admin'\nimport { bannersMessages } from './messages'\n\nconst seeded = new WeakSet<object>()\n\n/** Puts this package's English under the panel's dictionary, once per panel. */\nexport function useBannersMessages(): void {\n try {\n const { i18n } = useAdmin()\n\n if (!seeded.has(i18n)) {\n i18n.defaults('webx-banners', bannersMessages)\n seeded.add(i18n)\n }\n } catch {\n // Outside a panel there is no dictionary to seed, and `useTranslate` falls back on its own.\n }\n}\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport { onBeforeRouteLeave, useRoute, useRouter } from 'vue-router'\nimport {\n confirm,\n localizedValue,\n toast,\n useLocales,\n WxActionBar,\n WxBadge,\n WxButton,\n WxCard,\n WxFormItem,\n WxSelect,\n WxSkeleton,\n type LocalizedValue,\n} from '@webx-ui/core'\nimport {\n useAdmin,\n useErrorText,\n useTranslate,\n WxSaveState,\n WxScreen,\n WxScreenHead,\n type ScreenAction,\n useBodyKeys,\n} from '@webx-ui/module-admin'\nimport type { Patch, ScreenModel } from '@webx-ui/schema'\nimport { createBannersApi } from './api'\nimport { provideBannerEditor } from './editor'\nimport { defaultLayers } from './layers'\nimport { useBannersMessages } from './i18n'\nimport type { BannerDetail, BannerSummary, PlaceRow } from './types'\n\n/**\n * One banner, on a page of its own: a head, the place, the described screen, and one button.\n *\n * A page and not a pane beside the list, because the form is long — three pictures, the words,\n * up to three buttons — and `banners.form` is a described screen a project adds its own fields\n * to (decision 14). One explicit save, no autosave: a banner has no draft (decision 13), so what\n * is saved is on the site at once, if it is switched on.\n *\n * The place is not a field of the screen (decision 9). The list of places is alive — own places\n * come and go — and the options of a described select are patched on when the server boots, so a\n * live list has nowhere to go there. It is a select of its own above the screen, and rides beside\n * the values: `PUT { values, place }`. Changing it marks the form changed like any field does.\n *\n * A new banner is a form first and a record on the first save: pressing \"New banner\" and walking\n * away should not leave an empty, switched-off row at the end of a place.\n */\ndefineOptions({ name: 'WxBannerEditorPage' })\n\nconst props = withDefaults(defineProps<{ base?: string }>(), { base: '/banners' })\n\nconst admin = useAdmin()\nconst api = createBannersApi(admin)\nconst route = useRoute()\nconst router = useRouter()\nconst locales = useLocales()\nuseBannersMessages()\n\nconst t = useTranslate('webx-banners')\n/* Not the server's `message`: the panel says how a request failed in its own words. */\nconst message = useErrorText()\n\n/** `null` — a banner not written yet (`/banners/new`). */\nconst id = computed<number | null>(() => {\n const raw = route.params.id\n\n return typeof raw === 'string' && raw !== '' ? Number(raw) : null\n})\n\nconst banner = ref<BannerSummary | null>(null)\nconst values = ref<ScreenModel>({})\nconst snapshot = ref('')\nconst place = ref<string | null>(null)\n/** The place the banner stands in on the server — what \"changed\" and \"back\" are measured by. */\nconst placeSaved = ref<string | null>(null)\nconst places = ref<PlaceRow[]>([])\n\nconst loading = ref(true)\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\n\nconst canManage = computed(() => admin.can('banners.manage'))\n\nconst current = computed(() => JSON.stringify(values.value))\nconst dirty = computed(\n () =>\n snapshot.value !== '' && (current.value !== snapshot.value || place.value !== placeSaved.value),\n)\n\nconst state = computed<'saving' | 'unsaved' | 'saved'>(() => {\n if (saving.value) return 'saving'\n\n return dirty.value || id.value === null ? 'unsaved' : 'saved'\n})\n\n/** The title follows the field rather than the answer: an edit shows at the top as it is typed. */\nconst title = computed(() => {\n const written = values.value.title as LocalizedValue | string | null | undefined\n const fallback = id.value === null ? t('banner.new') : (banner.value?.title ?? '')\n\n return localizedValue(written, locales.active.value, fallback) || t('banner.untitled')\n})\n\n/** Back to the place the banner stands in, and not to the section's first one. */\nconst back = computed(() => {\n const key = placeSaved.value ?? place.value\n\n return key === null ? props.base : `${props.base}?place=${encodeURIComponent(key)}`\n})\n\nconst section = computed(\n () =>\n admin.state.manifest?.modules.find((module) => module.id === 'banners')?.title ??\n t('module.banners'),\n)\n\n/** The place the banner stands in now — the select's, not the server's — with its layers' frame. */\nconst placeRow = computed(() => places.value.find((one) => one.key === place.value) ?? null)\n\nprovideBannerEditor({ values, place: placeRow })\n\n/* The layers' card only where the place prints layers (§4.4 of the layers spec). */\nconst screenPatch = computed<Patch>(() =>\n placeRow.value?.layers ? [] : [{ op: 'remove', target: 'layers-card' }],\n)\n\n/**\n * A banner of a place with layers that was never laid out starts with its title, its text and its\n * buttons as three layers (decision 5). `[]` is somebody having taken them all away, and stays.\n */\nfunction withDefaultLayers(model: ScreenModel): ScreenModel {\n if (!placeRow.value?.layers || (model.layers !== null && model.layers !== undefined)) return model\n\n return { ...model, layers: defaultLayers(locales.list.value[0]?.code ?? locales.active.value) }\n}\n\nconst placeOptions = computed(() =>\n places.value.map((one) => ({ value: one.key, label: `${one.title} · ${one.key}` })),\n)\n\nconst placeModel = computed<string | number | null>({\n get: () => place.value,\n set: (value) => {\n place.value = value === null || value === undefined ? null : String(value)\n },\n})\n\n/** What the server would answer for a banner with nothing in it yet (§5.6). */\nfunction blank(): ScreenModel {\n return {\n image: null,\n image_mobile: null,\n video: null,\n title: {},\n text: {},\n buttons: [],\n layers: null,\n enabled: false,\n }\n}\n\nfunction take(detail: BannerDetail): void {\n banner.value = detail.banner\n place.value = detail.banner.place\n placeSaved.value = detail.banner.place\n\n // The default layers are part of what the banner opens with, not a change somebody made.\n const opened = withDefaultLayers(detail.values)\n\n values.value = opened\n snapshot.value = JSON.stringify(opened)\n}\n\nasync function load(): Promise<void> {\n loading.value = true\n\n try {\n const [list, detail] = await Promise.all([\n api.places(),\n id.value === null ? Promise.resolve(null) : api.get(id.value),\n ])\n\n places.value = list\n\n if (detail !== null) {\n take(detail)\n } else {\n const asked = route.query.place\n const key = typeof asked === 'string' && list.some((one) => one.key === asked) ? asked : null\n banner.value = null\n place.value = key ?? list[0]?.key ?? null\n placeSaved.value = place.value\n\n const fresh = withDefaultLayers(blank())\n\n values.value = fresh\n snapshot.value = JSON.stringify(fresh)\n }\n } catch (error) {\n toast.danger(message(error))\n void router.push(props.base)\n } finally {\n loading.value = false\n }\n}\n\nasync function save(): Promise<void> {\n if (saving.value || !canManage.value || place.value === null) return\n\n saving.value = true\n errors.value = {}\n\n try {\n const made = id.value === null\n const detail = made\n ? await api.create(place.value, values.value)\n : await api.save(\n id.value as number,\n values.value,\n place.value !== placeSaved.value ? place.value : undefined,\n )\n\n take(detail)\n toast.success(t('banner.saved'))\n\n // A record now, with an address of its own; `replace`, so \"back\" does not reopen a blank form.\n if (made) void router.replace(`${props.base}/${detail.banner.id}`)\n } catch (error) {\n const body = (error as { body?: { errors?: Record<string, string[]> } }).body\n\n if (body?.errors) {\n errors.value = body.errors\n toast.danger(t('banner.save-failed'))\n } else {\n toast.danger(message(error))\n }\n } finally {\n saving.value = false\n }\n}\n\nasync function remove(): Promise<void> {\n const record = banner.value\n\n if (!record) return\n\n const agreed = await confirm({\n title: t('banner.delete-title', { name: record.title }),\n message: t('banner.delete-text'),\n confirmText: t('banner.delete'),\n cancelText: t('banner.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.remove(record.id)\n toast.success(t('banner.deleted'))\n // Nothing left to protect: the record is in the bin, and asking about its unsaved edits on\n // the way out would be asking about a banner that is gone.\n snapshot.value = current.value\n place.value = placeSaved.value\n void router.push(back.value)\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\n/* The browser's own guard: it cannot wait for a request, so all it can do is ask. */\nfunction leaveGuard(event: BeforeUnloadEvent): void {\n if (dirty.value) event.preventDefault()\n}\n\n/* Ctrl+S saves, as it does everywhere else that has a save button. */\nfunction onKeydown(event: KeyboardEvent): void {\n if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 's') {\n event.preventDefault()\n if (dirty.value || id.value === null) void save()\n }\n}\n\n// And from the page around the form, where a click on empty space leaves the focus.\nuseBodyKeys(onKeydown)\n\n/*\n * `/banners/new` and `/banners/5` are one component on two routes, and the router patches it\n * rather than mounting it again (CLAUDE.md §4) — so a change of banner is watched for. The first\n * save moving the address to the record it just made is not a change: that banner is on screen.\n */\n/* Moved to a place with layers, a banner never laid out gets the default three there too. */\nwatch(place, () => {\n if (!loading.value) values.value = withDefaultLayers(values.value)\n})\n\nwatch(id, (next) => {\n if (next !== null && banner.value?.id === next) return\n\n void load()\n})\n\nonMounted(() => {\n void load()\n window.addEventListener('beforeunload', leaveGuard)\n})\n\nonBeforeUnmount(() => {\n window.removeEventListener('beforeunload', leaveGuard)\n})\n\nonBeforeRouteLeave(async () => {\n if (!dirty.value || !canManage.value) return true\n\n return await confirm({\n title: t('banner.leave-title'),\n message: t('banner.leave-text'),\n confirmText: t('banner.leave'),\n cancelText: t('banner.cancel'),\n tone: 'danger',\n })\n})\n\n/* What leads away from the banner. On a phone the head folds it into the ···. */\nconst actions = computed<ScreenAction[]>(() =>\n canManage.value && banner.value !== null\n ? [\n {\n key: 'delete',\n label: t('banner.delete'),\n icon: 'trash',\n danger: true,\n menu: true,\n run: () => void remove(),\n },\n ]\n : [],\n)\n</script>\n\n<template>\n <div class=\"wx-banner-editor\" @keydown=\"onKeydown\">\n <template v-if=\"loading\">\n <!-- Shaped like the screen it stands in for: the head, and a card for what is coming. -->\n <wx-skeleton class=\"wx-banner-editor__ghost\" title :rows=\"1\" />\n <wx-card><wx-skeleton :rows=\"6\" /></wx-card>\n </template>\n\n <template v-else>\n <wx-screen-head divider :back=\"back\" :back-label=\"section\" :title=\"title\" :actions=\"actions\">\n <template v-if=\"banner && !banner.enabled\" #title-after>\n <wx-badge dot>{{ t('banner.disabled') }}</wx-badge>\n </template>\n </wx-screen-head>\n\n <wx-card class=\"wx-banner-editor__place\">\n <wx-form-item\n :label=\"t('banner.place')\"\n :help=\"id === null ? undefined : t('banner.place-help')\"\n :error=\"errors.place?.[0]\"\n required\n >\n <wx-select\n v-model=\"placeModel\"\n :options=\"placeOptions\"\n :disabled=\"!canManage || saving\"\n :aria-label=\"t('banner.place')\"\n />\n </wx-form-item>\n </wx-card>\n\n <wx-screen\n v-model=\"values\"\n name=\"banners.form\"\n :patch=\"screenPatch\"\n :errors=\"errors\"\n :disabled=\"!canManage || saving\"\n />\n\n <wx-action-bar v-if=\"canManage\">\n <template #state>\n <wx-save-state :state=\"state\" />\n </template>\n\n <wx-button\n type=\"primary\"\n :loading=\"saving\"\n :disabled=\"(!dirty && id !== null) || place === null\"\n @click=\"save\"\n >\n {{ t('banner.save') }}\n </wx-button>\n </wx-action-bar>\n </template>\n </div>\n</template>\n\n<style scoped>\n.wx-banner-editor {\n display: flex;\n flex-direction: column;\n gap: var(--wx-gap, var(--wx-space-16));\n container-type: inline-size;\n}\n\n.wx-banner-editor__ghost {\n max-width: 420px;\n}\n\n/* A select as wide as a place's name needs, not as the page: it is one choice, not a text. */\n.wx-banner-editor__place :deep(.wx-form-item) {\n max-width: 420px;\n margin-bottom: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { nextTick, onMounted, ref, useTemplateRef } from 'vue'\nimport {\n useLocales,\n useModal,\n WxButton,\n WxDialog,\n WxFormItem,\n WxInput,\n WxSpace,\n type LocalizedValue,\n} from '@webx-ui/core'\nimport { useAdmin, useTranslate } from '@webx-ui/module-admin'\nimport { createBannersApi } from './api'\nimport { useBannersMessages } from './i18n'\nimport type { PlaceRow } from './types'\n\n/**\n * A place of somebody's own: a name, and a key when it is new (§5.4).\n *\n * The key does not follow the name the way a page's address follows its title: it is typed into\n * `banners('…')` by hand, and a key that quietly became `promo-2` would be a template asking for\n * a place that is not there. For the same reason a rename never touches it.\n *\n * The name is translated, and required in the default language — which is what the list falls\n * back on in a panel open in a language nobody named the place in.\n */\ndefineOptions({ name: 'WxBannerPlaceDialog' })\n\nconst props = withDefaults(defineProps<{ place?: PlaceRow | null }>(), { place: null })\n\nconst { open, resolve, dismiss } = useModal<PlaceRow>()\n\nconst api = createBannersApi(useAdmin())\nconst locales = useLocales()\nuseBannersMessages()\n\nconst t = useTranslate('webx-banners')\n\nconst editing = props.place !== null\n\n/*\n * Every language when the server sent them (`titles`), the one the list shows otherwise — filed\n * under the default language, which is where a place with one name has it.\n */\nfunction initial(): LocalizedValue {\n if (props.place === null) return {}\n if (props.place.titles) return { ...props.place.titles }\n\n const first = locales.list.value[0]?.code\n\n return first === undefined ? {} : { [first]: props.place.title }\n}\n\nconst key = ref(props.place?.key ?? '')\nconst title = ref<LocalizedValue>(initial())\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\nconst field = useTemplateRef<HTMLElement>('field')\n\nonMounted(async () => {\n await nextTick()\n field.value?.querySelector('input')?.focus()\n})\n\n/** Something written in at least one language: the server says which one it needs. */\nfunction written(): boolean {\n return Object.values(title.value).some((words) => words.trim() !== '')\n}\n\n/** The first line of a refusal under a field that may be named by language: `title.en`. */\nfunction errorOf(name: string): string | undefined {\n const found = Object.keys(errors.value).find((one) => one === name || one.startsWith(`${name}.`))\n\n return found === undefined ? undefined : errors.value[found]?.[0]\n}\n\nasync function submit(): Promise<void> {\n if (!written() || saving.value) return\n\n saving.value = true\n errors.value = {}\n\n try {\n resolve(\n editing && props.place !== null\n ? await api.renamePlace(props.place.key, title.value)\n : await api.createPlace({ key: key.value.trim(), title: title.value }),\n )\n } catch (error) {\n // Refused under its own field: a key that is taken or spelled in a way no template could\n // write, a name missing in the default language.\n errors.value = (error as { body?: { errors?: Record<string, string[]> } }).body?.errors ?? {}\n } finally {\n saving.value = false\n }\n}\n</script>\n\n<template>\n <wx-dialog\n v-model:open=\"open\"\n :title=\"editing ? t('places.rename-title') : t('places.new-title')\"\n :width=\"440\"\n >\n <wx-form-item :label=\"t('places.field-title')\" :error=\"errorOf('title')\" required>\n <div ref=\"field\">\n <wx-input\n v-model=\"title\"\n localized\n :aria-label=\"t('places.field-title')\"\n @keyup.enter=\"submit\"\n />\n </div>\n </wx-form-item>\n\n <wx-form-item\n :label=\"t('places.field-key')\"\n :error=\"errorOf('key')\"\n :help=\"editing ? undefined : t('places.key-help')\"\n required\n >\n <wx-input\n v-model=\"key\"\n :disabled=\"editing\"\n :aria-label=\"t('places.field-key')\"\n autocomplete=\"off\"\n @keyup.enter=\"submit\"\n />\n </wx-form-item>\n\n <template #footer>\n <wx-space size=\"sm\">\n <wx-button variant=\"outline\" @click=\"dismiss()\">{{ t('places.cancel') }}</wx-button>\n <wx-button\n type=\"primary\"\n :loading=\"saving\"\n :disabled=\"!written() || key.trim() === ''\"\n @click=\"submit\"\n >\n {{ editing ? t('places.save') : t('places.create') }}\n </wx-button>\n </wx-space>\n </template>\n </wx-dialog>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\nimport { useRoute, useRouter } from 'vue-router'\nimport {\n confirm,\n toast,\n WxAlert,\n WxBadge,\n WxEmpty,\n WxIcon,\n WxSegmented,\n WxSkeleton,\n WxSortableList,\n WxTooltip,\n type SegmentedValue,\n} from '@webx-ui/core'\nimport {\n useAdmin,\n useErrorText,\n useTranslate,\n WxRowMenu,\n WxScreenHead,\n type RowAction,\n type ScreenAction,\n} from '@webx-ui/module-admin'\nimport { createBannersApi } from './api'\nimport { useBannersMessages } from './i18n'\nimport type { BannerRow, PlaceRow } from './types'\n\n/**\n * The banners of one place: the right-hand half of the section, and the whole screen on a phone.\n *\n * The order here is the order on the site, and a place is small, so the list is the whole place\n * and a drag writes it whole (`reorder`). The bin is the same list turned over: nothing to drag,\n * and a banner in it opens nothing — it can only come back.\n *\n * The way back is drawn here and not by the pane around it: the drawer a narrow screen opens this\n * in has no close of its own on purpose (CLAUDE.md §4).\n */\ndefineOptions({ name: 'WxBannerPlacePane' })\n\nconst props = withDefaults(\n defineProps<{\n place: PlaceRow\n base: string\n /** False in the drawer a narrow screen opens this in. */\n inline?: boolean\n }>(),\n { inline: true },\n)\n\nconst emit = defineEmits<{\n back: []\n /** Something moved in or out of the place: the count beside its name is stale. */\n changed: []\n}>()\n\nconst admin = useAdmin()\nconst api = createBannersApi(admin)\nconst route = useRoute()\nconst router = useRouter()\nuseBannersMessages()\n\nconst t = useTranslate('webx-banners')\n/* Not the server's `message`: the panel says how a request failed in its own words. */\nconst message = useErrorText()\n\nconst rows = ref<BannerRow[]>([])\nconst loading = ref(true)\n\nconst canManage = computed(() => admin.can('banners.manage'))\n\n/* The bin is in the address with the place, so coming back from a banner lands where one was. */\nconst view = computed<SegmentedValue>({\n get: () => (route.query.view === 'trashed' ? 'trashed' : 'live'),\n set: (value) => {\n void router.replace({\n query: { ...route.query, view: value === 'trashed' ? 'trashed' : undefined },\n })\n },\n})\n\nconst inBin = computed(() => view.value === 'trashed')\n\nconst views = computed(() => [\n { value: 'live', label: t('banner.all') },\n { value: 'trashed', label: t('banner.trashed'), icon: 'trash' as const },\n])\n\nconst sortable = computed(() => canManage.value && !inBin.value && rows.value.length > 1)\n\nconst layout = computed(() => {\n const known = ['single', 'random', 'slider']\n\n return known.includes(props.place.layout) ? t(`places.layout-${props.place.layout}`) : ''\n})\n\nasync function load(): Promise<void> {\n loading.value = true\n\n try {\n rows.value = await api.list(props.place.key, inBin.value)\n } catch (error) {\n toast.danger(message(error))\n } finally {\n loading.value = false\n }\n}\n\nwatch(inBin, () => void load())\nvoid load()\n\nfunction edit(row: BannerRow): string {\n return `${props.base}/${row.id}`\n}\n\nfunction add(): void {\n void router.push({ path: `${props.base}/new`, query: { place: props.place.key } })\n}\n\n/**\n * The order on screen, written whole. The list is already in its new order (`WxSortableList`\n * reorders the model before it says so), so a failure has to put it back rather than leave the\n * screen disagreeing with the database.\n */\nasync function reorder(): Promise<void> {\n try {\n await api.reorder(\n props.place.key,\n rows.value.map((row) => row.id),\n )\n } catch (error) {\n toast.danger(message(error, t('banner.reorder-failed')))\n await load()\n }\n}\n\n/*\n * On and off from the list, through the form's own door: there is no switch of its own in the\n * API, and a second way to write a banner would be a second set of rules to keep in step. What\n * the form would refuse — a look the config dropped since — is refused here the same way.\n */\nasync function toggle(row: BannerRow): Promise<void> {\n try {\n const detail = await api.get(row.id)\n\n await api.save(row.id, { ...detail.values, enabled: !row.enabled })\n toast.success(row.enabled ? t('banner.switched-off') : t('banner.switched-on'))\n } catch (error) {\n toast.danger(message(error))\n } finally {\n await load()\n }\n}\n\nasync function remove(row: BannerRow): Promise<void> {\n const agreed = await confirm({\n title: t('banner.delete-title', { name: row.title }),\n message: t('banner.delete-text'),\n confirmText: t('banner.delete'),\n cancelText: t('banner.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.remove(row.id)\n toast.success(t('banner.deleted'))\n emit('changed')\n } catch (error) {\n toast.danger(message(error))\n } finally {\n await load()\n }\n}\n\nasync function restore(row: BannerRow): Promise<void> {\n try {\n await api.restore(row.id)\n toast.success(t('banner.restored'))\n emit('changed')\n } catch (error) {\n toast.danger(message(error))\n } finally {\n await load()\n }\n}\n\nfunction actionsFor(row: BannerRow): RowAction[] {\n if (inBin.value) {\n return canManage.value\n ? [\n {\n key: 'restore',\n icon: 'refresh',\n label: t('banner.restore'),\n run: () => void restore(row),\n },\n ]\n : []\n }\n\n const open: RowAction = {\n key: 'open',\n icon: 'edit',\n label: t('banner.open'),\n run: () => void router.push(edit(row)),\n }\n\n if (!canManage.value) return [open]\n\n return [\n open,\n {\n key: 'toggle',\n icon: row.enabled ? 'eye-off' : 'eye',\n label: row.enabled ? t('banner.disable') : t('banner.enable'),\n run: () => void toggle(row),\n },\n {\n key: 'delete',\n icon: 'trash',\n label: t('banner.delete'),\n danger: true,\n run: () => void remove(row),\n },\n ]\n}\n\n/* The one thing this pane exists for, in its head; on a narrow pane it stays a button. */\nconst actions = computed<ScreenAction[]>(() =>\n canManage.value && !inBin.value\n ? [{ key: 'new', label: t('banner.new'), icon: 'plus', primary: true, run: add }]\n : [],\n)\n</script>\n\n<template>\n <div class=\"wx-banner-place\">\n <wx-screen-head\n :level=\"3\"\n :back=\"!props.inline\"\n :back-label=\"t('places.places')\"\n :title=\"place.title\"\n :actions=\"actions\"\n :collapse-below=\"420\"\n @back=\"emit('back')\"\n >\n <template v-if=\"place.declared\" #title-after>\n <wx-tooltip :content=\"t('places.declared-help')\">\n <span class=\"wx-banner-place__lock\" tabindex=\"0\" :aria-label=\"t('places.declared')\">\n <wx-icon name=\"lock\" size=\"sm\" />\n </span>\n </wx-tooltip>\n </template>\n </wx-screen-head>\n\n <!--\n How a template asks for this place, where somebody writing one will look for it: the key\n is the whole contract between the panel and the site (decision 7).\n -->\n <p class=\"wx-banner-place__usage\">\n <span>{{ t('places.usage') }}</span>\n <code>banners('{{ place.key }}')</code>\n <span v-if=\"layout\" class=\"wx-banner-place__layout\">· {{ layout }}</span>\n </p>\n\n <wx-segmented\n v-model=\"view\"\n class=\"wx-banner-place__views\"\n size=\"sm\"\n :options=\"views\"\n :aria-label=\"t('banner.all')\"\n />\n\n <wx-skeleton v-if=\"loading && rows.length === 0\" :rows=\"4\" />\n\n <wx-empty\n v-else-if=\"rows.length === 0\"\n :title=\"inBin ? t('banner.empty-bin') : t('banner.empty')\"\n :description=\"inBin ? undefined : t('banner.empty-help')\"\n />\n\n <template v-else>\n <!-- A grip and not the whole row: the row opens a banner, and a row that both opens and\n drags is one where one of the two happens by accident. -->\n <wx-sortable-list\n v-model=\"rows\"\n class=\"wx-banner-place__list\"\n hoverable\n :class=\"{ 'is-loading': loading }\"\n plain\n item-key=\"id\"\n :handle=\"sortable ? 'grip' : 'row'\"\n :item-label=\"(item: BannerRow) => item.title\"\n :disabled=\"!sortable\"\n @move=\"reorder\"\n >\n <template #default=\"{ item }\">\n <component\n :is=\"inBin ? 'div' : 'router-link'\"\n :to=\"inBin ? undefined : edit(item)\"\n class=\"wx-banner-row\"\n :class=\"{ 'is-off': !item.enabled && !inBin }\"\n >\n <span class=\"wx-banner-row__thumb\">\n <img v-if=\"item.thumb\" :src=\"item.thumb\" alt=\"\" loading=\"lazy\" />\n <wx-icon v-else name=\"image\" size=\"sm\" :aria-label=\"t('banner.no-picture')\" />\n </span>\n\n <span class=\"wx-banner-row__body\">\n <span class=\"wx-banner-row__title\">{{ item.title }}</span>\n\n <span v-if=\"item.video || (!item.enabled && !inBin)\" class=\"wx-banner-row__facts\">\n <wx-badge v-if=\"item.video\" size=\"sm\" type=\"info\">{{ t('banner.video') }}</wx-badge>\n <wx-badge v-if=\"!item.enabled && !inBin\" dot size=\"sm\">{{\n t('banner.disabled')\n }}</wx-badge>\n </span>\n </span>\n </component>\n </template>\n\n <template #actions=\"{ item }\">\n <wx-row-menu :actions=\"actionsFor(item)\" :label=\"item.title\" />\n </template>\n </wx-sortable-list>\n\n <!-- What a drag does here, said once under the list. -->\n <wx-alert\n v-if=\"sortable\"\n class=\"wx-banner-place__note\"\n type=\"info\"\n :description=\"t('banner.order')\"\n />\n </template>\n </div>\n</template>\n\n<style scoped>\n.wx-banner-place {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-12);\n min-width: 0;\n padding: var(--wx-gap, var(--wx-space-16));\n}\n\n.wx-banner-place__lock {\n display: inline-flex;\n color: var(--wx-text-muted);\n}\n\n.wx-banner-place__usage {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--wx-space-6);\n margin: 0;\n color: var(--wx-text-muted);\n font-size: var(--wx-font-size-sm);\n}\n\n.wx-banner-place__usage code {\n padding: 0 var(--wx-space-4);\n border-radius: var(--wx-radius-xs);\n background: var(--wx-bg-subtle);\n color: var(--wx-text-default);\n font-family: var(--wx-font-family-mono);\n /* Copied whole, as somebody writing a template would: a click selects all of it. */\n user-select: all;\n}\n\n.wx-banner-place__views {\n align-self: flex-start;\n}\n\n.wx-banner-place__list.is-loading {\n opacity: 0.6;\n}\n\n.wx-banner-place__list :deep(.wx-sortable-list__row) {\n gap: var(--wx-space-4);\n padding-inline: var(--wx-space-4);\n}\n\n.wx-banner-row {\n display: flex;\n align-items: center;\n gap: var(--wx-space-12);\n /* The row is the target: a short title should not leave half the line dead. */\n flex: 1 1 auto;\n width: 100%;\n min-width: 0;\n padding-block: var(--wx-space-8);\n color: inherit;\n text-decoration: none;\n}\n\n/* Off is not gone: still here, still in its place, just quieter than what is on the site. */\n.wx-banner-row.is-off .wx-banner-row__thumb,\n.wx-banner-row.is-off .wx-banner-row__title {\n opacity: 0.55;\n}\n\n.wx-banner-row__thumb {\n display: flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 80px;\n aspect-ratio: 16 / 9;\n overflow: hidden;\n border-radius: var(--wx-radius-xs);\n background: var(--wx-bg-fill);\n color: var(--wx-text-muted);\n}\n\n.wx-banner-row__thumb img {\n width: 100%;\n height: 100%;\n /* A thumbnail wider than its frame would otherwise hold the row open (CLAUDE.md §4). */\n min-width: 0;\n object-fit: cover;\n}\n\n.wx-banner-row__body {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n gap: var(--wx-space-4);\n min-width: 0;\n}\n\n.wx-banner-row__title {\n min-width: 0;\n overflow: hidden;\n font-weight: var(--wx-font-weight-medium);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\na.wx-banner-row:hover .wx-banner-row__title {\n color: var(--wx-color-primary);\n}\n\n.wx-banner-row__facts {\n display: flex;\n flex-wrap: wrap;\n gap: var(--wx-space-4);\n}\n\n.wx-banner-place__note {\n margin-top: var(--wx-space-4);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\nimport { useRoute, useRouter, type LocationQueryRaw } from 'vue-router'\nimport {\n confirm,\n createModal,\n toast,\n WxCard,\n WxEmpty,\n WxIcon,\n WxIndicator,\n WxListDetail,\n WxSkeleton,\n WxText,\n} from '@webx-ui/core'\nimport {\n useAdmin,\n useErrorText,\n useTranslate,\n WxListScreen,\n WxRowMenu,\n type RowAction,\n type ScreenAction,\n} from '@webx-ui/module-admin'\nimport { createBannersApi } from './api'\nimport { useBannersMessages } from './i18n'\nimport PlaceDialog from './PlaceDialog.vue'\nimport PlacePane from './PlacePane.vue'\nimport type { PlaceRow } from './types'\n\n/**\n * The section: the places of the site on the left, the banners of one of them on the right\n * (§5.4) — the shape of the menus, for the same reason: somebody who opens this is arranging one\n * place, not choosing between them, so the first one opens by itself where there is room for it.\n * On a phone it does not: there the same line would raise a panel over a list nobody touched.\n *\n * A declared place is on the list before anything was ever saved into it (decision 1) — its row\n * in the table is made by its first banner — so places are named by key throughout, not by id.\n */\nconst props = withDefaults(defineProps<{ base?: string }>(), { base: '/banners' })\n\nconst admin = useAdmin()\nconst api = createBannersApi(admin)\nconst route = useRoute()\nconst router = useRouter()\nuseBannersMessages()\n\nconst t = useTranslate('webx-banners')\n/* Not the server's `message`: the panel says how a request failed in its own words. */\nconst message = useErrorText()\n\nconst places = ref<PlaceRow[]>([])\nconst loading = ref(true)\nconst open = ref(false)\nconst detailInline = ref(true)\n\nconst canManage = computed(() => admin.can('banners.manage'))\n\nconst edit = createModal<PlaceRow, { place?: PlaceRow | null }>(PlaceDialog)\n\nconst title = computed(\n () =>\n admin.state.manifest?.modules.find((module) => module.id === 'banners')?.title ??\n t('module.banners'),\n)\n\n/** Which place is open, kept in the address — so \"back\" from a banner lands on its place. */\nconst current = computed<string | null>(() => {\n const asked = route.query.place\n\n return typeof asked === 'string' && asked !== '' ? asked : null\n})\n\nconst chosen = computed(() => places.value.find((place) => place.key === current.value) ?? null)\n\nasync function load(): Promise<void> {\n loading.value = true\n\n try {\n places.value = await api.places()\n } catch (error) {\n toast.danger(message(error))\n } finally {\n loading.value = false\n }\n}\n\nvoid load()\n\n/*\n * The first place, where there is a column to draw it in — only once the width is measured\n * (`detail-inline` says so), so a phone does not get a drawer over a list it never touched.\n */\nwatch([places, chosen, detailInline], () => {\n if (!detailInline.value || places.value.length === 0 || chosen.value !== null) return\n\n void router.replace({ query: { ...route.query, place: places.value[0]!.key } })\n})\n\nwatch(chosen, (place) => {\n if (place !== null && detailInline.value) open.value = true\n})\n\n/* Coming back from a banner on a phone lands on its place, and that is the drawer, open. */\nif (current.value !== null) open.value = true\n\nfunction choose(place: PlaceRow): void {\n const query: LocationQueryRaw = { ...route.query, place: place.key }\n\n // The bin of one place is not the bin of the next.\n delete query.view\n void router.replace({ query })\n open.value = true\n}\n\nasync function add(): Promise<void> {\n const made = await edit({ place: null })\n\n if (!made) return\n\n toast.success(t('places.created'))\n await load()\n choose(made)\n}\n\nasync function rename(place: PlaceRow): Promise<void> {\n const saved = await edit({ place })\n\n if (!saved) return\n\n toast.success(t('places.renamed'))\n await load()\n}\n\nasync function remove(place: PlaceRow): Promise<void> {\n const agreed = await confirm({\n title: t('places.delete-title', { place: place.title }),\n message: t('places.delete-text'),\n confirmText: t('places.delete'),\n cancelText: t('places.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.removePlace(place.key)\n toast.success(t('places.deleted'))\n\n if (current.value === place.key) {\n const rest = { ...route.query }\n\n delete rest.place\n delete rest.view\n void router.replace({ query: rest })\n open.value = false\n }\n } catch (error) {\n // The one refusal worth its own words: something is still in it — perhaps only in the bin,\n // which the count beside the name does not show (§5.6).\n toast.danger(message(error))\n } finally {\n await load()\n }\n}\n\nfunction actionsFor(place: PlaceRow): RowAction[] {\n if (!canManage.value || place.declared) return []\n\n return [\n { key: 'rename', icon: 'edit', label: t('places.rename'), run: () => void rename(place) },\n {\n key: 'delete',\n icon: 'trash',\n // Said in the line itself, since a switched-off line cannot carry a tooltip: an empty\n // place is the only kind that goes (decision 1).\n label: place.count > 0 ? t('places.delete-empty-first') : t('places.delete'),\n danger: true,\n disabled: place.count > 0,\n run: () => void remove(place),\n },\n ]\n}\n\nconst actions = computed<ScreenAction[]>(() =>\n canManage.value\n ? [{ key: 'new-place', label: t('places.new'), icon: 'plus', run: () => void add() }]\n : [],\n)\n</script>\n\n<template>\n <wx-list-screen :title=\"title\" :actions=\"actions\" :card=\"false\">\n <wx-card class=\"wx-banners\" padding=\"none\">\n <wx-list-detail\n v-model:open=\"open\"\n class=\"wx-banners__panes\"\n :list-width=\"300\"\n :detail-min=\"440\"\n :detail-label=\"t('places.places')\"\n @detail-inline=\"detailInline = $event\"\n >\n <template #list>\n <wx-skeleton\n v-if=\"loading && places.length === 0\"\n class=\"wx-banners__loading\"\n :rows=\"3\"\n />\n\n <wx-empty\n v-else-if=\"places.length === 0\"\n :title=\"t('places.empty')\"\n :description=\"t('places.empty-help')\"\n />\n\n <ul v-else class=\"wx-banners__list\">\n <li v-for=\"place in places\" :key=\"place.key\" class=\"wx-banners__row\">\n <button\n type=\"button\"\n class=\"wx-banners__open\"\n :class=\"{ 'is-current': place.key === current }\"\n @click=\"choose(place)\"\n >\n <span class=\"wx-banners__name\">\n <wx-text truncate weight=\"medium\">{{ place.title }}</wx-text>\n <wx-icon\n v-if=\"place.declared\"\n class=\"wx-banners__lock\"\n name=\"lock\"\n size=\"sm\"\n :aria-label=\"t('places.declared')\"\n />\n <wx-indicator\n class=\"wx-banners__count\"\n type=\"neutral\"\n :value=\"place.count\"\n :label=\"t('places.count')\"\n />\n </span>\n\n <wx-text size=\"sm\" tone=\"muted\" mono truncate>banners('{{ place.key }}')</wx-text>\n </button>\n\n <wx-row-menu\n v-if=\"actionsFor(place).length > 0\"\n :actions=\"actionsFor(place)\"\n :label=\"place.title\"\n />\n </li>\n </ul>\n </template>\n\n <template #detail=\"{ inline, back }\">\n <place-pane\n v-if=\"chosen\"\n :key=\"chosen.key\"\n :place=\"chosen\"\n :base=\"props.base\"\n :inline=\"inline\"\n @back=\"back\"\n @changed=\"load\"\n />\n </template>\n\n <template #empty>\n <wx-empty :title=\"t('places.choose')\" :description=\"t('places.choose-help')\" />\n </template>\n </wx-list-detail>\n </wx-card>\n </wx-list-screen>\n</template>\n\n<style>\n/*\n * What scrolls here is the page, the way it does on every other list of the panel. `clip`, not\n * `hidden`: `hidden` would make this a scroll container, and everything sticky inside would pin\n * itself to a box that never moves (CLAUDE.md §4).\n */\n.wx-banners > .wx-card__body {\n display: flex;\n flex-direction: column;\n overflow: clip;\n border-radius: inherit;\n}\n</style>\n\n<style scoped>\n.wx-banners__loading {\n padding: var(--wx-space-16);\n}\n\n.wx-banners__list {\n margin: 0;\n padding: var(--wx-space-8);\n list-style: none;\n}\n\n.wx-banners__row {\n display: flex;\n align-items: center;\n gap: var(--wx-space-4);\n margin: 0;\n padding-inline: var(--wx-space-8);\n border-radius: var(--wx-radius-sm);\n}\n\n.wx-banners__row:has(.is-current) {\n background: var(--wx-bg-subtle);\n}\n\n.wx-banners__open {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--wx-space-2);\n flex: 1 1 auto;\n min-width: 0;\n padding-block: var(--wx-space-8);\n padding-inline: 0;\n border: 0;\n background: none;\n font: inherit;\n color: inherit;\n text-align: start;\n cursor: pointer;\n}\n\n.wx-banners__open.is-current {\n color: var(--wx-color-primary);\n}\n\n.wx-banners__name {\n display: flex;\n align-items: center;\n gap: var(--wx-space-6);\n min-width: 0;\n max-width: 100%;\n}\n\n.wx-banners__lock {\n flex: none;\n color: var(--wx-text-muted);\n}\n\n.wx-banners__count {\n flex: 0 0 auto;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport {\n useLocales,\n WxButton,\n WxColorPicker,\n WxFormItem,\n WxIcon,\n WxInput,\n WxInputNumber,\n WxSegmented,\n WxSelect,\n WxSwitch,\n WxText,\n WxTextarea,\n type LocalizedValue,\n} from '@webx-ui/core'\nimport { useTranslate, WxLinkField, type LinkValue } from '@webx-ui/module-admin'\nimport { WxMediaField } from '@webx-ui/module-media'\nimport {\n clone,\n ENTRANCES,\n FRAME_LIMITS,\n LOOPS,\n ORIGINS,\n unknownFields,\n type BannerLayer,\n type LayerFrame,\n type LayerOrigin,\n type LayerStyle,\n type LayerView,\n type MediaRef,\n} from './layers'\n\n/**\n * What the selected layer is, in fields (§5.4 of the layers spec): its words, its geometry on the\n * layout being edited, its look and its motion. Every length is a percent of the picture's width.\n *\n * It never writes the layer itself: a changed layer goes up whole (`update`), a changed number of\n * the geometry as a patch of the layout on screen (`frame`) — the canvas decides which layout\n * that is, and copies the computer's to the phone on the phone's first change.\n */\ndefineOptions({ name: 'WxBannerLayerInspector' })\n\nconst props = defineProps<{\n layer: BannerLayer\n view: LayerView\n tags: { value: string; label: string }[]\n variants: { value: string; label: string }[]\n palette: { key: string; value: string }[]\n disabled?: boolean\n}>()\n\nconst emit = defineEmits<{\n update: [layer: BannerLayer]\n frame: [patch: Partial<LayerFrame>]\n 'same-as-desktop': []\n delete: []\n}>()\n\nconst t = useTranslate('webx-banners')\nconst locales = useLocales()\n\nconst current = computed<LayerFrame>(() =>\n props.view === 'mobile'\n ? (props.layer.frames.mobile ?? props.layer.frames.desktop)\n : props.layer.frames.desktop,\n)\n\nconst ownPhone = computed(() => props.view === 'mobile' && props.layer.frames.mobile !== null)\n\nfunction change(mutate: (layer: BannerLayer) => void): void {\n const next = clone(props.layer)\n\n mutate(next)\n emit('update', next)\n}\n\nfunction setStyle<K extends keyof LayerStyle>(key: K, value: LayerStyle[K]): void {\n change((layer) => {\n layer.style[key] = value\n })\n}\n\nconst hasWords = computed(() => props.layer.type === 'text' || props.layer.type === 'button')\n\nconst text = computed<LocalizedValue>({\n get: () => props.layer.text ?? {},\n set: (value) =>\n change((layer) => {\n layer.text = value\n }),\n})\n\nconst name = computed<string>({\n get: () => props.layer.name ?? '',\n set: (value) =>\n change((layer) => {\n layer.name = value\n }),\n})\n\n/** What is on screen in the active language — and which `{…}` in it is not a field. */\nconst typos = computed(() => unknownFields(text.value[locales.active.value] ?? ''))\nconst typoList = computed(() => typos.value.map((one) => `{${one}}`).join(', '))\n\n/**\n * The language whose words a reader of the active one sees when it is empty (decision 6), for\n * the field's hint.\n */\nconst fallsBackTo = computed(() => {\n const map = text.value\n const active = locales.active.value\n\n if ((map[active] ?? '').trim() !== '') return null\n\n return (\n [...locales.list.value.map((one) => one.code), ...Object.keys(map)].find(\n (code) => (map[code] ?? '').trim() !== '',\n ) ?? null\n )\n})\n\nfunction insert(field: 'title' | 'text'): void {\n const code = locales.active.value\n const words = text.value[code] ?? ''\n\n text.value = { ...text.value, [code]: `${words}${words === '' ? '' : ' '}{${field}}` }\n}\n\nconst image = computed<MediaRef | null>({\n get: () => props.layer.image ?? null,\n set: (value) =>\n change((layer) => {\n layer.image = value\n }),\n})\n\nconst link = computed<LinkValue | null>({\n get: () => props.layer.link ?? null,\n set: (value) =>\n change((layer) => {\n layer.link = value\n }),\n})\n\nconst choice = <T,>(get: () => T, set: (value: T) => void) =>\n computed<string | number | null>({\n get: () => get() as unknown as string | number | null,\n set: (value) => set(value as unknown as T),\n })\n\nconst tag = choice(\n () => props.layer.tag ?? 'p',\n (value: string) =>\n change((layer) => {\n layer.tag = value\n }),\n)\n\nconst variant = choice(\n () => props.layer.variant ?? props.variants[0]?.value ?? null,\n (value: string | null) =>\n change((layer) => {\n layer.variant = value\n }),\n)\n\nconst font = choice(\n () => props.layer.style.font ?? 'site',\n (value: string) => setStyle('font', value === 'heading' || value === 'body' ? value : null),\n)\n\nconst weight = choice(\n () => props.layer.style.weight,\n (value: number) => setStyle('weight', Number(value)),\n)\n\nconst align = computed<string | number>({\n get: () => props.layer.style.align,\n set: (value) => setStyle('align', value as LayerStyle['align']),\n})\n\nconst entrance = choice(\n () => props.layer.motion.in ?? 'none',\n (value: string) =>\n change((layer) => {\n layer.motion.in = (ENTRANCES as readonly string[]).includes(value)\n ? (value as BannerLayer['motion']['in'])\n : null\n }),\n)\n\nconst loop = choice(\n () => props.layer.motion.loop ?? 'none',\n (value: string) =>\n change((layer) => {\n layer.motion.loop = (LOOPS as readonly string[]).includes(value)\n ? (value as BannerLayer['motion']['loop'])\n : null\n }),\n)\n\ntype FrameNumber = keyof typeof FRAME_LIMITS\n\n/** The numbers of the geometry this kind of layer has. */\nconst numbers = computed<FrameNumber[]>(() => {\n switch (props.layer.type) {\n case 'image':\n return ['x', 'y', 'w', 'radius', 'rotate']\n case 'buttons':\n return ['x', 'y', 'w', 'size', 'gap', 'rotate']\n default:\n return ['x', 'y', 'w', 'size', 'pad', 'radius', 'tracking', 'rotate']\n }\n})\n\nconst steps: Record<FrameNumber, number> = {\n x: 0.05,\n y: 0.05,\n w: 0.5,\n size: 0.05,\n pad: 0.05,\n radius: 0.1,\n tracking: 0.01,\n rotate: 1,\n gap: 0.1,\n}\n\nfunction setNumber(key: FrameNumber, value: number | null): void {\n if (value === null || Number.isNaN(value)) return\n\n emit('frame', { [key]: value })\n}\n\nfunction setOrigin(origin: LayerOrigin): void {\n emit('frame', { origin })\n}\n\nconst fontOptions = computed(() => [\n { value: 'site', label: t('layers.font-site') },\n { value: 'heading', label: t('layers.font-heading') },\n { value: 'body', label: t('layers.font-body') },\n])\n\nconst weightOptions = [100, 200, 300, 400, 500, 600, 700, 800, 900].map((value) => ({\n value,\n label: String(value),\n}))\n\nconst alignOptions = computed(() => [\n { value: 'start', label: t('layers.align-start') },\n { value: 'center', label: t('layers.align-center') },\n { value: 'end', label: t('layers.align-end') },\n])\n\nconst entranceOptions = computed(() => [\n { value: 'none', label: t('layers.motion-none') },\n ...ENTRANCES.map((value) => ({ value, label: t(`layers.in-${value}`) })),\n])\n\nconst loopOptions = computed(() => [\n { value: 'none', label: t('layers.motion-none') },\n ...LOOPS.map((value) => ({ value, label: t(`layers.loop-${value}`) })),\n])\n\n/* A colour is a swatch of the theme's palette (it follows the theme) or one of its own. */\nfunction colorOf(key: 'color' | 'background'): string | null {\n const value = props.layer.style[key]\n\n if (value === null) return null\n if (value.startsWith('token:')) {\n return props.palette.find((one) => `token:${one.key}` === value)?.value ?? null\n }\n\n return value\n}\n\nfunction pickColor(key: 'color' | 'background', value: string | null): void {\n const hex =\n typeof value === 'string' && /^#[0-9a-f]{6}([0-9a-f]{2})?$/i.test(value)\n ? value.toLowerCase()\n : null\n\n setStyle(key, hex)\n}\n</script>\n\n<template>\n <div class=\"wx-banner-layer-inspector\">\n <div class=\"wx-banner-layer-inspector__head\">\n <wx-input\n v-model=\"name\"\n size=\"sm\"\n :placeholder=\"t('layers.name')\"\n :aria-label=\"t('layers.name')\"\n :disabled=\"disabled\"\n />\n <wx-button size=\"sm\" variant=\"outline\" danger :disabled=\"disabled\" @click=\"emit('delete')\">\n <template #icon><wx-icon name=\"trash\" /></template>\n {{ t('layers.delete') }}\n </wx-button>\n </div>\n\n <template v-if=\"hasWords\">\n <wx-form-item\n :label=\"layer.type === 'button' ? t('layers.label') : t('layers.words')\"\n :help=\"\n fallsBackTo\n ? t('layers.falls-back', { code: fallsBackTo.toUpperCase() })\n : t('layers.words-help')\n \"\n >\n <wx-textarea\n v-if=\"layer.type === 'text'\"\n v-model=\"text\"\n localized\n :rows=\"2\"\n :disabled=\"disabled\"\n />\n <wx-input v-else v-model=\"text\" localized :disabled=\"disabled\" />\n </wx-form-item>\n <div class=\"wx-banner-layer-inspector__fields\">\n <wx-button size=\"sm\" variant=\"text\" :disabled=\"disabled\" @click=\"insert('title')\"\n >+ {title}</wx-button\n >\n <wx-button size=\"sm\" variant=\"text\" :disabled=\"disabled\" @click=\"insert('text')\"\n >+ {text}</wx-button\n >\n </div>\n <wx-text v-if=\"typos.length > 0\" size=\"sm\" tone=\"warning\">\n {{ t('layers.unknown-field', { fields: typoList }) }}\n </wx-text>\n </template>\n\n <wx-form-item v-if=\"layer.type === 'text'\" :label=\"t('layers.tag')\">\n <wx-select v-model=\"tag\" :options=\"tags\" size=\"sm\" :disabled=\"disabled\" />\n </wx-form-item>\n\n <wx-form-item v-if=\"layer.type === 'image'\" :label=\"t('layers.image')\">\n <wx-media-field\n v-model=\"image\"\n accept=\"image\"\n :aspect=\"null\"\n :height=\"120\"\n :disabled=\"disabled\"\n />\n </wx-form-item>\n\n <template v-if=\"layer.type === 'button'\">\n <wx-form-item :label=\"t('layers.link')\">\n <wx-link-field v-model=\"link\" :allow-none=\"false\" :disabled=\"disabled\" />\n </wx-form-item>\n <wx-form-item :label=\"t('layers.variant')\">\n <wx-select v-model=\"variant\" :options=\"variants\" size=\"sm\" :disabled=\"disabled\" />\n </wx-form-item>\n </template>\n\n <wx-text v-if=\"layer.type === 'buttons'\" size=\"sm\" tone=\"muted\">{{\n t('layers.buttons-help')\n }}</wx-text>\n\n <section class=\"wx-banner-layer-inspector__group\">\n <div class=\"wx-banner-layer-inspector__title\">\n <wx-text size=\"sm\" strong>\n {{\n view === 'mobile'\n ? ownPhone\n ? t('layers.geometry-mobile-own')\n : t('layers.geometry-mobile-same')\n : t('layers.geometry-desktop')\n }}\n </wx-text>\n <wx-button\n v-if=\"ownPhone\"\n size=\"sm\"\n variant=\"text\"\n :disabled=\"disabled\"\n @click=\"emit('same-as-desktop')\"\n >\n {{ t('layers.same-as-desktop') }}\n </wx-button>\n </div>\n <wx-text size=\"sm\" tone=\"muted\">{{ t('layers.unit') }}</wx-text>\n\n <div class=\"wx-banner-layer-inspector__grid\">\n <wx-form-item v-for=\"key in numbers\" :key=\"key\" :label=\"t(`layers.${key}`)\">\n <wx-input-number\n :model-value=\"current[key]\"\n :min=\"FRAME_LIMITS[key][0]\"\n :max=\"FRAME_LIMITS[key][1]\"\n :step=\"steps[key]\"\n :precision=\"2\"\n size=\"sm\"\n :disabled=\"disabled\"\n @update:model-value=\"\n (value: number | null | undefined) => setNumber(key, value ?? null)\n \"\n />\n </wx-form-item>\n </div>\n\n <wx-form-item :label=\"t('layers.origin')\" :help=\"t('layers.origin-help')\">\n <div\n class=\"wx-banner-layer-inspector__origins\"\n role=\"radiogroup\"\n :aria-label=\"t('layers.origin')\"\n >\n <button\n v-for=\"origin in ORIGINS\"\n :key=\"origin\"\n type=\"button\"\n role=\"radio\"\n :aria-checked=\"current.origin === origin\"\n :aria-label=\"origin\"\n :disabled=\"disabled\"\n :class=\"[\n 'wx-banner-layer-inspector__origin',\n { 'is-active': current.origin === origin },\n ]\"\n @click=\"setOrigin(origin)\"\n />\n </div>\n </wx-form-item>\n\n <wx-switch\n :model-value=\"current.hidden\"\n :label=\"t('layers.hidden-here')\"\n :disabled=\"disabled\"\n @update:model-value=\"(value) => emit('frame', { hidden: value === true })\"\n />\n </section>\n\n <section v-if=\"layer.type !== 'image'\" class=\"wx-banner-layer-inspector__group\">\n <wx-text size=\"sm\" strong>{{ t('layers.look') }}</wx-text>\n\n <div class=\"wx-banner-layer-inspector__grid\">\n <wx-form-item :label=\"t('layers.font')\">\n <wx-select v-model=\"font\" :options=\"fontOptions\" size=\"sm\" :disabled=\"disabled\" />\n </wx-form-item>\n <wx-form-item :label=\"t('layers.weight')\">\n <wx-select v-model=\"weight\" :options=\"weightOptions\" size=\"sm\" :disabled=\"disabled\" />\n </wx-form-item>\n <wx-form-item :label=\"t('layers.line-height')\">\n <wx-input-number\n :model-value=\"layer.style.line_height\"\n :min=\"0.8\"\n :max=\"3\"\n :step=\"0.05\"\n :precision=\"2\"\n size=\"sm\"\n :disabled=\"disabled\"\n @update:model-value=\"\n (value: number | null | undefined) => value != null && setStyle('line_height', value)\n \"\n />\n </wx-form-item>\n <wx-form-item :label=\"t('layers.opacity')\">\n <wx-input-number\n :model-value=\"layer.style.opacity\"\n :min=\"0\"\n :max=\"1\"\n :step=\"0.05\"\n :precision=\"2\"\n size=\"sm\"\n :disabled=\"disabled\"\n @update:model-value=\"\n (value: number | null | undefined) => value != null && setStyle('opacity', value)\n \"\n />\n </wx-form-item>\n </div>\n\n <wx-form-item :label=\"t('layers.align')\">\n <wx-segmented v-model=\"align\" :options=\"alignOptions\" size=\"sm\" :disabled=\"disabled\" />\n </wx-form-item>\n\n <div class=\"wx-banner-layer-inspector__switches\">\n <wx-switch\n :model-value=\"layer.style.italic\"\n :label=\"t('layers.italic')\"\n :disabled=\"disabled\"\n @update:model-value=\"(value) => setStyle('italic', value === true)\"\n />\n <wx-switch\n :model-value=\"layer.style.uppercase\"\n :label=\"t('layers.uppercase')\"\n :disabled=\"disabled\"\n @update:model-value=\"(value) => setStyle('uppercase', value === true)\"\n />\n <wx-switch\n :model-value=\"layer.style.shadow\"\n :label=\"t('layers.shadow')\"\n :disabled=\"disabled\"\n @update:model-value=\"(value) => setStyle('shadow', value === true)\"\n />\n </div>\n\n <wx-form-item\n v-for=\"key in ['color', 'background'] as const\"\n :key=\"key\"\n :label=\"t(`layers.${key}`)\"\n >\n <div class=\"wx-banner-layer-inspector__color\">\n <button\n v-for=\"swatch in palette\"\n :key=\"swatch.key\"\n type=\"button\"\n :title=\"swatch.key\"\n :aria-label=\"swatch.key\"\n :disabled=\"disabled\"\n :class=\"[\n 'wx-banner-layer-inspector__swatch',\n { 'is-active': layer.style[key] === `token:${swatch.key}` },\n ]\"\n :style=\"{ background: swatch.value }\"\n @click=\"setStyle(key, `token:${swatch.key}`)\"\n />\n <wx-color-picker\n :model-value=\"colorOf(key)\"\n clearable\n size=\"sm\"\n :disabled=\"disabled\"\n @update:model-value=\"(value: string | null) => pickColor(key, value)\"\n />\n </div>\n </wx-form-item>\n </section>\n\n <section class=\"wx-banner-layer-inspector__group\">\n <wx-text size=\"sm\" strong>{{ t('layers.motion') }}</wx-text>\n <div class=\"wx-banner-layer-inspector__grid\">\n <wx-form-item :label=\"t('layers.entrance')\">\n <wx-select v-model=\"entrance\" :options=\"entranceOptions\" size=\"sm\" :disabled=\"disabled\" />\n </wx-form-item>\n <wx-form-item :label=\"t('layers.loop')\">\n <wx-select v-model=\"loop\" :options=\"loopOptions\" size=\"sm\" :disabled=\"disabled\" />\n </wx-form-item>\n <wx-form-item :label=\"t('layers.delay')\">\n <wx-input-number\n :model-value=\"layer.motion.delay\"\n :min=\"0\"\n :max=\"10000\"\n :step=\"50\"\n size=\"sm\"\n :disabled=\"disabled || layer.motion.in === null\"\n @update:model-value=\"\n (value: number | null | undefined) =>\n value != null && change((one) => (one.motion.delay = value))\n \"\n />\n </wx-form-item>\n <wx-form-item :label=\"t('layers.duration')\">\n <wx-input-number\n :model-value=\"layer.motion.duration\"\n :min=\"100\"\n :max=\"5000\"\n :step=\"50\"\n size=\"sm\"\n :disabled=\"disabled || layer.motion.in === null\"\n @update:model-value=\"\n (value: number | null | undefined) =>\n value != null && change((one) => (one.motion.duration = value))\n \"\n />\n </wx-form-item>\n </div>\n </section>\n </div>\n</template>\n\n<style scoped>\n.wx-banner-layer-inspector {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-12);\n min-width: 0;\n}\n\n.wx-banner-layer-inspector__head {\n display: flex;\n gap: var(--wx-space-8);\n}\n\n.wx-banner-layer-inspector__fields {\n display: flex;\n gap: var(--wx-space-4);\n margin-top: calc(-1 * var(--wx-space-8));\n}\n\n.wx-banner-layer-inspector__group {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-8);\n padding-top: var(--wx-space-12);\n border-top: 1px solid var(--wx-border-default);\n}\n\n.wx-banner-layer-inspector__title {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--wx-space-8);\n}\n\n.wx-banner-layer-inspector__grid {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 0 var(--wx-space-8);\n}\n\n.wx-banner-layer-inspector__grid :deep(.wx-form-item),\n.wx-banner-layer-inspector :deep(.wx-form-item) {\n margin-bottom: var(--wx-space-4);\n}\n\n.wx-banner-layer-inspector__switches {\n display: flex;\n flex-wrap: wrap;\n gap: var(--wx-space-12);\n}\n\n.wx-banner-layer-inspector__origins {\n display: grid;\n grid-template-columns: repeat(3, 18px);\n gap: var(--wx-space-4);\n}\n\n.wx-banner-layer-inspector__origin {\n width: 18px;\n height: 18px;\n padding: 0;\n border: 1px solid var(--wx-border-default);\n border-radius: var(--wx-radius-xs);\n background: var(--wx-bg-surface);\n cursor: pointer;\n}\n\n.wx-banner-layer-inspector__origin.is-active {\n border-color: var(--wx-color-primary);\n background: var(--wx-color-primary);\n}\n\n.wx-banner-layer-inspector__color {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--wx-space-4);\n}\n\n.wx-banner-layer-inspector__swatch {\n width: 22px;\n height: 22px;\n padding: 0;\n border: 1px solid var(--wx-border-default);\n border-radius: var(--wx-radius-full);\n cursor: pointer;\n}\n\n.wx-banner-layer-inspector__swatch.is-active {\n outline: 2px solid var(--wx-color-primary);\n outline-offset: 1px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'\nimport {\n useLocales,\n WxButton,\n WxIcon,\n WxSegmented,\n WxSortableList,\n WxText,\n WxTooltip,\n} from '@webx-ui/core'\nimport { useAdmin, useTranslate } from '@webx-ui/module-admin'\nimport { createMediaApi } from '@webx-ui/module-media'\nimport { createBannersApi } from './api'\nimport { useBannerEditor } from './editor'\nimport { useBannersMessages } from './i18n'\nimport LayerInspector from './LayerInspector.vue'\nimport {\n clampFrame,\n clone,\n cropBox,\n layerClasses,\n layerVars,\n layerWords,\n newLayer,\n ratioOf,\n type BannerLayer,\n type LayerFrame,\n type LayerType,\n type LayerView,\n} from './layers'\nimport type { BannerButton, BannersTheme } from './types'\nimport '../../../php/packages/module-banners/resources/css/layers.css'\n\n/**\n * The layers of a banner, edited on its picture (§5 of the layers spec).\n *\n * The canvas is the site: the same `layers.css`, the same custom properties the server prints,\n * every length a percent of the picture's width — so the width of this column changes the scale\n * and nothing else. It shows the whole picture and darkens what the frame of the place crops\n * away; a layer under the shade is not seen on the site.\n *\n * A field of `banners.form` like any other: the value is the list of layers, and every change is\n * a new list. Undo is this field's own — Ctrl+Z on the canvas — because a text input keeps its\n * own and the two must not fight.\n */\ndefineOptions({ name: 'WxBannerLayers' })\n\nconst props = defineProps<{\n modelValue?: BannerLayer[] | null\n disabled?: boolean\n}>()\n\nconst emit = defineEmits<{ 'update:modelValue': [value: BannerLayer[]] }>()\n\nconst admin = useAdmin()\nconst editor = useBannerEditor()\nconst locales = useLocales()\nconst media = createMediaApi(admin)\nconst api = createBannersApi(admin)\nuseBannersMessages()\n\nconst t = useTranslate('webx-banners')\n\nconst layers = computed<BannerLayer[]>(() => props.modelValue ?? [])\nconst place = computed(() => editor?.place.value?.layers ?? null)\n\nconst view = ref<LayerView>('desktop')\nconst selectedId = ref<string | null>(null)\nconst selected = computed(() => layers.value.find((layer) => layer.id === selectedId.value) ?? null)\n\nconst locale = computed(() => {\n const active = locales.active.value\n\n return locales.list.value.some((one) => one.code === active)\n ? active\n : (locales.list.value[0]?.code ?? 'en')\n})\nconst order = computed(() => locales.list.value.map((one) => one.code))\n\n/* --- History ---------------------------------------------------------------------------- */\n\nconst past = ref<string[]>([])\nconst future = ref<string[]>([])\n\nfunction remember(): void {\n past.value = [...past.value.slice(-99), JSON.stringify(layers.value)]\n future.value = []\n}\n\nfunction commit(next: BannerLayer[], record = true): void {\n if (record) remember()\n\n emit('update:modelValue', next)\n}\n\nfunction undo(): void {\n const last = past.value.at(-1)\n\n if (last === undefined) return\n\n future.value = [...future.value, JSON.stringify(layers.value)]\n past.value = past.value.slice(0, -1)\n emit('update:modelValue', JSON.parse(last) as BannerLayer[])\n}\n\nfunction redo(): void {\n const next = future.value.at(-1)\n\n if (next === undefined) return\n\n past.value = [...past.value, JSON.stringify(layers.value)]\n future.value = future.value.slice(0, -1)\n emit('update:modelValue', JSON.parse(next) as BannerLayer[])\n}\n\n/* --- Changing layers --------------------------------------------------------------------- */\n\nfunction replace(id: string, change: (layer: BannerLayer) => BannerLayer, record = true): void {\n commit(\n layers.value.map((layer) => (layer.id === id ? change(clone(layer)) : layer)),\n record,\n )\n}\n\n/** The layout being edited; the phone's is the computer's until it is changed (decision 3). */\nfunction frameOf(layer: BannerLayer, which: LayerView = view.value): LayerFrame {\n return which === 'mobile' ? (layer.frames.mobile ?? layer.frames.desktop) : layer.frames.desktop\n}\n\nfunction setFrame(id: string, patch: Partial<LayerFrame>, record = true): void {\n replace(\n id,\n (layer) => {\n const next = clampFrame({ ...frameOf(layer), ...patch })\n\n if (view.value === 'mobile') layer.frames.mobile = next\n else layer.frames.desktop = next\n\n return layer\n },\n record,\n )\n}\n\n/* Inspector edits typed in a row are one step of history, not one per key. */\nlet lastEdit = { id: '', at: 0 }\n\nfunction edit(next: BannerLayer): void {\n const now = Date.now()\n const merge = lastEdit.id === next.id && now - lastEdit.at < 800\n\n lastEdit = { id: next.id, at: now }\n replace(next.id, () => next, !merge)\n}\n\nfunction add(type: LayerType): void {\n const layer = newLayer(\n type,\n layers.value.map((one) => one.id),\n locale.value,\n )\n\n commit([layer, ...layers.value])\n selectedId.value = layer.id\n}\n\nfunction remove(id: string): void {\n commit(layers.value.filter((layer) => layer.id !== id))\n\n if (selectedId.value === id) selectedId.value = null\n}\n\nfunction duplicate(id: string): void {\n const index = layers.value.findIndex((layer) => layer.id === id)\n const source = layers.value[index]\n\n if (!source || source.type === 'buttons') return\n\n const copy = clone(source)\n\n copy.id = newLayer(\n 'text',\n layers.value.map((one) => one.id),\n locale.value,\n ).id\n copy.frames.desktop = {\n ...copy.frames.desktop,\n x: copy.frames.desktop.x + 2,\n y: copy.frames.desktop.y + 2,\n }\n\n const next = [...layers.value]\n\n next.splice(index, 0, copy)\n commit(next)\n selectedId.value = copy.id\n}\n\nfunction toggleHidden(layer: BannerLayer): void {\n setFrame(layer.id, { hidden: !frameOf(layer).hidden })\n}\n\nconst hasButtonsLayer = computed(() => layers.value.some((layer) => layer.type === 'buttons'))\n\nconst listModel = computed<BannerLayer[]>({\n get: () => layers.value,\n set: (next) => commit(next),\n})\n\n/* --- The pictures ------------------------------------------------------------------------ */\n\ninterface Picture {\n url: string\n ratio: number | null\n}\n\nconst pictures = reactive<Record<string, Picture | null>>({})\n\nfunction pathOf(value: unknown): string | null {\n const path = (value as { path?: unknown } | null)?.path\n\n return typeof path === 'string' && path !== '' ? path : null\n}\n\nasync function resolve(path: string | null): Promise<void> {\n if (path === null || path in pictures) return\n\n pictures[path] = null\n\n try {\n const file = await media.fileByPath(path)\n\n pictures[path] = file\n ? { url: file.url, ratio: file.width && file.height ? file.width / file.height : null }\n : null\n } catch {\n pictures[path] = null\n }\n}\n\nconst desktopPath = computed(() => pathOf(editor?.values.value.image))\nconst mobilePath = computed(() => pathOf(editor?.values.value.image_mobile))\n\nwatch(\n () => [desktopPath.value, mobilePath.value, ...layers.value.map((layer) => pathOf(layer.image))],\n (paths) => paths.forEach((path) => void resolve(path)),\n { immediate: true },\n)\n\nconst frameRatio = computed(() =>\n view.value === 'mobile'\n ? ratioOf(place.value?.ratio_mobile, 0.8)\n : ratioOf(place.value?.ratio, 16 / 6),\n)\n\nconst background = computed<Picture | null>(() => {\n const mobile = mobilePath.value === null ? null : (pictures[mobilePath.value] ?? null)\n const desktop = desktopPath.value === null ? null : (pictures[desktopPath.value] ?? null)\n\n return view.value === 'mobile' ? (mobile ?? desktop) : desktop\n})\n\n/* The canvas shows the whole picture: its own proportions, the frame's crop drawn over it. */\nconst imageRatio = computed(() => background.value?.ratio ?? frameRatio.value)\nconst crop = computed(() => cropBox(imageRatio.value, frameRatio.value))\n\nconst frameStyle = computed(() => {\n const ratio = String(imageRatio.value)\n\n return { '--ratio': ratio, '--ratio-mobile': ratio, '--img': ratio, '--img-m': ratio }\n})\n\n/* --- What a layer shows ------------------------------------------------------------------ */\n\n/* Exactly the language asked for: a field with no words in it puts in nothing, as on the site. */\nconst ownFields = computed(() => {\n const own = (field: 'title' | 'text'): string => {\n const value = editor?.values.value[field] as Record<string, string> | undefined\n\n return (value?.[locale.value] ?? '').trim()\n }\n\n return { title: own('title'), text: own('text') }\n})\n\nfunction wordsOf(layer: BannerLayer): string {\n return layerWords(layer.text, locale.value, order.value, ownFields.value)\n}\n\nconst bannerButtons = computed(() =>\n ((editor?.values.value.buttons as BannerButton[] | undefined) ?? [])\n .map((button) => ({\n label: (button.label?.[locale.value] ?? '').trim(),\n variant: button.variant ?? place.value?.variants[0]?.value ?? 'primary',\n }))\n .filter((button) => button.label !== ''),\n)\n\nfunction tagOf(layer: BannerLayer): string {\n return layer.type === 'text' && /^(p|span|h[1-6])$/.test(layer.tag ?? '')\n ? (layer.tag as string)\n : 'div'\n}\n\nfunction labelOf(layer: BannerLayer): string {\n if (layer.name) return layer.name\n\n const own = layer.text?.[locale.value] ?? Object.values(layer.text ?? {})[0] ?? ''\n\n if (own.trim() === '{title}') return t('layers.title-layer')\n if (own.trim() === '{text}') return t('layers.text-layer')\n if (layer.type === 'buttons') return t('layers.buttons-layer')\n if (layer.type === 'image') return t('layers.image')\n\n return own.trim() || t(`layers.${layer.type}`)\n}\n\nconst icons: Record<LayerType, string> = {\n text: 'file-text',\n image: 'image',\n button: 'link',\n buttons: 'grid',\n}\n\n/** A layer the frame hides on this layout: wholly under the shade. */\nfunction outside(layer: BannerLayer): boolean {\n const one = frameOf(layer)\n const height = 100 / imageRatio.value\n const box = crop.value\n const left = box.left\n const right = box.left + box.width\n const top = (box.top / 100) * height\n const bottom = ((box.top + box.height) / 100) * height\n\n // The box itself, not its anchor: a layer anchored on the right reaches left of its x.\n const x = one.x + (originShift(one.origin)[0] / 100) * one.w\n\n return x > right || x + one.w < left || one.y > bottom || one.y < top - 50\n}\n\n/* --- Pointer: move and resize ------------------------------------------------------------ */\n\nconst stage = ref<HTMLElement | null>(null)\nconst canvas = ref<HTMLElement | null>(null)\nconst guides = ref<{ axis: 'x' | 'y'; at: number }[]>([])\n\n/** How far the pointer must go before a press becomes a drag, in px. */\nconst SLOP = 3\n/** How close an edge must come to a guide to stick to it, in px. */\nconst SNAP = 6\n\nfunction snapTargets(axis: 'x' | 'y', skip: string): number[] {\n const height = 100 / imageRatio.value\n const box = crop.value\n const targets =\n axis === 'x'\n ? [0, 50, 100, box.left, box.left + box.width]\n : [0, height / 2, height, (box.top / 100) * height, ((box.top + box.height) / 100) * height]\n\n for (const layer of layers.value) {\n if (layer.id === skip) continue\n\n const one = frameOf(layer)\n\n if (axis === 'x') targets.push(one.x, one.x + one.w)\n else targets.push(one.y)\n }\n\n return targets\n}\n\nfunction snap(\n axis: 'x' | 'y',\n value: number,\n size: number,\n unit: number,\n skip: string,\n free: boolean,\n): number {\n if (free) return value\n\n const edges = [0, size / 2, size]\n let best: { delta: number; at: number } | null = null\n\n for (const target of snapTargets(axis, skip)) {\n for (const edge of edges) {\n const delta = target - (value + edge)\n\n if (\n Math.abs(delta) * unit <= SNAP &&\n (best === null || Math.abs(delta) < Math.abs(best.delta))\n ) {\n best = { delta, at: target }\n }\n }\n }\n\n if (best === null) return value\n\n guides.value = [...guides.value.filter((one) => one.axis !== axis), { axis, at: best.at }]\n\n return value + best.delta\n}\n\nfunction select(layer: BannerLayer): void {\n selectedId.value = layer.id\n canvas.value?.focus({ preventScroll: true })\n}\n\nfunction press(event: PointerEvent, layer: BannerLayer, mode: 'move' | 'width' | 'scale'): void {\n select(layer)\n\n if (props.disabled || event.button !== 0 || !stage.value) return\n\n event.preventDefault()\n\n const rect = stage.value.getBoundingClientRect()\n const unit = rect.width / 100\n const element = (event.target as HTMLElement).closest('.webx-banner__layer') as HTMLElement | null\n const box = element?.getBoundingClientRect()\n const height = box ? box.height / unit : 0\n const from = { ...frameOf(layer) }\n const start = { x: event.clientX, y: event.clientY }\n let moving = false\n\n const move = (next: PointerEvent): void => {\n const dx = (next.clientX - start.x) / unit\n const dy = (next.clientY - start.y) / unit\n\n if (!moving) {\n if (Math.hypot(next.clientX - start.x, next.clientY - start.y) < SLOP) return\n\n moving = true\n remember()\n }\n\n guides.value = []\n\n if (mode === 'move') {\n // The box's own left and top, so its edges and its middle are what sticks.\n const shift = {\n x: (originShift(from.origin)[0] / 100) * from.w,\n y: (originShift(from.origin)[1] / 100) * height,\n }\n const left = snap('x', from.x + dx + shift.x, from.w, unit, layer.id, next.altKey)\n const top = snap('y', from.y + dy + shift.y, height, unit, layer.id, next.altKey)\n\n setFrame(layer.id, { x: left - shift.x, y: top - shift.y }, false)\n } else if (mode === 'width') {\n setFrame(layer.id, { w: Math.max(1, from.w + dx) }, false)\n } else {\n const factor = Math.max(0.05, (from.w + dx) / from.w)\n\n setFrame(layer.id, { w: from.w * factor, size: from.size * factor }, false)\n }\n }\n\n const up = (): void => {\n window.removeEventListener('pointermove', move)\n window.removeEventListener('pointerup', up)\n window.removeEventListener('pointercancel', up)\n guides.value = []\n }\n\n window.addEventListener('pointermove', move)\n window.addEventListener('pointerup', up)\n window.addEventListener('pointercancel', up)\n}\n\nfunction originShift(origin: LayerFrame['origin']): [number, number] {\n const x = origin.endsWith('r')\n ? -100\n : origin === 't' || origin === 'c' || origin === 'b'\n ? -50\n : 0\n const y = origin.startsWith('b')\n ? -100\n : origin === 'l' || origin === 'c' || origin === 'r'\n ? -50\n : 0\n\n return [x, y]\n}\n\nfunction pressCanvas(event: PointerEvent): void {\n if (!(event.target as HTMLElement).closest('.webx-banner__layer')) selectedId.value = null\n}\n\n/* --- Keyboard ---------------------------------------------------------------------------- */\n\nfunction onKeydown(event: KeyboardEvent): void {\n const mod = event.ctrlKey || event.metaKey\n const key = event.key.toLowerCase()\n\n if (mod && key === 'z') {\n event.preventDefault()\n\n if (event.shiftKey) redo()\n else undo()\n\n return\n }\n\n if (mod && key === 'y') {\n event.preventDefault()\n redo()\n\n return\n }\n\n const layer = selected.value\n\n if (!layer || props.disabled) return\n\n if (mod && key === 'd') {\n event.preventDefault()\n duplicate(layer.id)\n\n return\n }\n\n if (key === 'delete' || key === 'backspace') {\n event.preventDefault()\n remove(layer.id)\n\n return\n }\n\n if (key === 'escape') {\n selectedId.value = null\n\n return\n }\n\n const step = event.shiftKey ? 0.5 : 0.05\n const arrows: Record<string, [number, number]> = {\n arrowleft: [-step, 0],\n arrowright: [step, 0],\n arrowup: [0, -step],\n arrowdown: [0, step],\n }\n const by = arrows[key]\n\n if (!by) return\n\n event.preventDefault()\n\n const one = frameOf(layer)\n const now = Date.now()\n // A key held down is one step of history.\n const merge = lastEdit.id === `arrows:${layer.id}` && now - lastEdit.at < 800\n\n lastEdit = { id: `arrows:${layer.id}`, at: now }\n setFrame(layer.id, { x: one.x + by[0], y: one.y + by[1] }, !merge)\n}\n\n/* --- The theme --------------------------------------------------------------------------- */\n\nconst theme = ref<BannersTheme | null>(null)\nlet themeStyle: HTMLStyleElement | null = null\n\nonMounted(async () => {\n try {\n theme.value = await api.theme()\n } catch {\n theme.value = null\n }\n\n const css = `${theme.value?.tokens_css ?? ''}\\n${theme.value?.fonts_css ?? ''}`.trim()\n\n if (css !== '' && !document.getElementById('wx-banners-theme')) {\n themeStyle = document.createElement('style')\n themeStyle.id = 'wx-banners-theme'\n themeStyle.textContent = css\n document.head.append(themeStyle)\n }\n})\n\nonBeforeUnmount(() => {\n themeStyle?.remove()\n})\n\nconst viewOptions = computed(() => [\n { value: 'desktop', label: t('layers.desktop'), icon: 'monitor' as const },\n { value: 'mobile', label: t('layers.mobile'), icon: 'smartphone' as const },\n])\n\nconst viewModel = computed<string | number>({\n get: () => view.value,\n set: (value) => {\n view.value = value === 'mobile' ? 'mobile' : 'desktop'\n },\n})\n</script>\n\n<template>\n <div class=\"wx-banner-layers\" :class=\"{ 'is-disabled': disabled }\">\n <div class=\"wx-banner-layers__bar\">\n <wx-segmented\n v-model=\"viewModel\"\n :options=\"viewOptions\"\n size=\"sm\"\n :aria-label=\"t('layers.layout')\"\n />\n\n <div class=\"wx-banner-layers__tools\">\n <wx-tooltip :content=\"t('layers.undo')\">\n <wx-button\n size=\"sm\"\n variant=\"text\"\n :disabled=\"disabled || past.length === 0\"\n :aria-label=\"t('layers.undo')\"\n @click=\"undo\"\n >\n <template #icon><wx-icon name=\"rotate-left\" /></template>\n </wx-button>\n </wx-tooltip>\n <wx-tooltip :content=\"t('layers.redo')\">\n <wx-button\n size=\"sm\"\n variant=\"text\"\n :disabled=\"disabled || future.length === 0\"\n :aria-label=\"t('layers.redo')\"\n @click=\"redo\"\n >\n <template #icon><wx-icon name=\"rotate-right\" /></template>\n </wx-button>\n </wx-tooltip>\n <wx-button size=\"sm\" variant=\"outline\" :disabled=\"disabled\" @click=\"add('text')\">\n <template #icon><wx-icon name=\"plus\" /></template>\n {{ t('layers.text') }}\n </wx-button>\n <wx-button size=\"sm\" variant=\"outline\" :disabled=\"disabled\" @click=\"add('image')\">\n <template #icon><wx-icon name=\"plus\" /></template>\n {{ t('layers.image') }}\n </wx-button>\n <wx-button size=\"sm\" variant=\"outline\" :disabled=\"disabled\" @click=\"add('button')\">\n <template #icon><wx-icon name=\"plus\" /></template>\n {{ t('layers.button') }}\n </wx-button>\n <wx-button\n v-if=\"!hasButtonsLayer\"\n size=\"sm\"\n variant=\"outline\"\n :disabled=\"disabled\"\n @click=\"add('buttons')\"\n >\n <template #icon><wx-icon name=\"plus\" /></template>\n {{ t('layers.buttons-layer') }}\n </wx-button>\n </div>\n </div>\n\n <wx-text v-if=\"!place\" size=\"sm\" tone=\"muted\">{{ t('layers.not-here') }}</wx-text>\n\n <div class=\"wx-banner-layers__body\">\n <div class=\"wx-banner-layers__main\">\n <div\n ref=\"canvas\"\n class=\"wx-banner-layers__canvas\"\n :class=\"{ 'is-overflow': place?.overflow }\"\n tabindex=\"0\"\n :aria-label=\"t('layers.canvas')\"\n @pointerdown=\"pressCanvas\"\n @keydown=\"onKeydown\"\n >\n <div\n class=\"webx-banner wx-banner-layers__frame\"\n :class=\"{ 'is-mobile': view === 'mobile' }\"\n :style=\"frameStyle\"\n >\n <div class=\"webx-banner__picture\">\n <img v-if=\"background\" class=\"webx-banner__image\" :src=\"background.url\" alt=\"\" />\n <div v-else class=\"wx-banner-layers__empty\">{{ t('layers.no-picture') }}</div>\n </div>\n\n <div ref=\"stage\" class=\"webx-banner__stage\">\n <component\n :is=\"tagOf(layer)\"\n v-for=\"(layer, index) in layers\"\n :key=\"layer.id\"\n :class=\"[\n ...layerClasses(layer),\n { 'is-selected': layer.id === selectedId, 'is-outside': outside(layer) },\n ]\"\n :style=\"layerVars(layer, layers.length - index)\"\n @pointerdown.stop=\"press($event, layer, 'move')\"\n >\n <span\n v-if=\"layer.type === 'text'\"\n class=\"webx-banner__inner wx-banner-layers__words\"\n >{{ wordsOf(layer) || '…' }}</span\n >\n <img\n v-else-if=\"layer.type === 'image'\"\n class=\"webx-banner__inner\"\n :src=\"pictures[pathOf(layer.image) ?? '']?.url ?? ''\"\n alt=\"\"\n draggable=\"false\"\n />\n <span\n v-else-if=\"layer.type === 'button'\"\n class=\"webx-banner__inner webx-banner__buttons\"\n >\n <span\n :class=\"[\n 'webx-banner__button',\n `webx-banner__button--${layer.variant ?? place?.variants[0]?.value ?? 'primary'}`,\n ]\"\n >\n {{ wordsOf(layer) || '…' }}\n </span>\n </span>\n <span v-else class=\"webx-banner__inner webx-banner__buttons\">\n <span\n v-for=\"(button, at) in bannerButtons\"\n :key=\"at\"\n :class=\"['webx-banner__button', `webx-banner__button--${button.variant}`]\"\n >{{ button.label }}</span\n >\n <span v-if=\"bannerButtons.length === 0\" class=\"webx-banner__button\">{{\n t('layers.buttons-layer')\n }}</span>\n </span>\n\n <template v-if=\"layer.id === selectedId && !disabled\">\n <span\n class=\"wx-banner-layers__handle wx-banner-layers__handle--width\"\n @pointerdown.stop=\"press($event, layer, 'width')\"\n />\n <span\n class=\"wx-banner-layers__handle wx-banner-layers__handle--scale\"\n @pointerdown.stop=\"press($event, layer, 'scale')\"\n />\n </template>\n </component>\n </div>\n\n <div\n class=\"wx-banner-layers__crop\"\n :style=\"{\n left: `${crop.left}%`,\n top: `${crop.top}%`,\n width: `${crop.width}%`,\n height: `${crop.height}%`,\n }\"\n />\n\n <div\n v-for=\"guide in guides\"\n :key=\"guide.axis\"\n :class=\"['wx-banner-layers__guide', `wx-banner-layers__guide--${guide.axis}`]\"\n :style=\"\n guide.axis === 'x' ? { left: `${guide.at}%` } : { top: `calc(${guide.at} * 1cqw)` }\n \"\n />\n </div>\n </div>\n\n <wx-text size=\"sm\" tone=\"muted\">{{ t('layers.hint') }}</wx-text>\n\n <wx-sortable-list\n v-model=\"listModel\"\n class=\"wx-banner-layers__list\"\n item-key=\"id\"\n size=\"sm\"\n :disabled=\"disabled\"\n :empty-text=\"t('layers.empty')\"\n :aria-label=\"t('layers.list')\"\n >\n <template #default=\"{ item }\">\n <button\n type=\"button\"\n class=\"wx-banner-layers__row\"\n :class=\"{ 'is-selected': (item as BannerLayer).id === selectedId }\"\n @click=\"select(item as BannerLayer)\"\n >\n <wx-icon :name=\"icons[(item as BannerLayer).type] as 'image'\" />\n <span class=\"wx-banner-layers__name\">{{ labelOf(item as BannerLayer) }}</span>\n <wx-icon\n v-if=\"outside(item as BannerLayer)\"\n name=\"warning\"\n class=\"wx-banner-layers__warn\"\n :aria-label=\"t('layers.outside')\"\n />\n </button>\n </template>\n <template #actions=\"{ item }\">\n <wx-button\n size=\"sm\"\n variant=\"text\"\n :disabled=\"disabled\"\n :aria-label=\"\n frameOf(item as BannerLayer).hidden ? t('layers.show') : t('layers.hide')\n \"\n @click=\"toggleHidden(item as BannerLayer)\"\n >\n <template #icon>\n <wx-icon :name=\"frameOf(item as BannerLayer).hidden ? 'eye-off' : 'eye'\" />\n </template>\n </wx-button>\n <wx-button\n size=\"sm\"\n variant=\"text\"\n :disabled=\"disabled\"\n :aria-label=\"t('layers.delete')\"\n @click=\"remove((item as BannerLayer).id)\"\n >\n <template #icon><wx-icon name=\"trash\" /></template>\n </wx-button>\n </template>\n </wx-sortable-list>\n </div>\n\n <layer-inspector\n v-if=\"selected\"\n class=\"wx-banner-layers__inspector\"\n :layer=\"selected\"\n :view=\"view\"\n :tags=\"place?.tags ?? []\"\n :variants=\"place?.variants ?? []\"\n :palette=\"theme?.palette ?? []\"\n :disabled=\"disabled\"\n @update=\"edit\"\n @frame=\"(patch: Partial<LayerFrame>) => setFrame(selected!.id, patch)\"\n @same-as-desktop=\"\n replace(selected!.id, (layer) => ({\n ...layer,\n frames: { ...layer.frames, mobile: null },\n }))\n \"\n @delete=\"remove(selected!.id)\"\n />\n <wx-text v-else class=\"wx-banner-layers__inspector\" size=\"sm\" tone=\"muted\">\n {{ t('layers.choose') }}\n </wx-text>\n </div>\n </div>\n</template>\n\n<style scoped>\n.wx-banner-layers {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-12);\n container-type: inline-size;\n min-width: 0;\n}\n\n.wx-banner-layers__bar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: var(--wx-space-8);\n}\n\n.wx-banner-layers__tools {\n display: flex;\n flex-wrap: wrap;\n gap: var(--wx-space-4);\n}\n\n.wx-banner-layers__body {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n gap: var(--wx-space-16);\n}\n\n@container (min-width: 900px) {\n .wx-banner-layers__body {\n grid-template-columns: minmax(0, 1fr) 320px;\n align-items: start;\n }\n}\n\n.wx-banner-layers__main {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-8);\n min-width: 0;\n}\n\n.wx-banner-layers__canvas {\n position: relative;\n /* The layers, the shade and the guides stack inside the canvas, never over the sticky bar. */\n isolation: isolate;\n overflow: hidden;\n border-radius: var(--wx-radius-sm);\n background: var(--wx-bg-muted, var(--wx-bg-surface));\n outline-offset: 2px;\n user-select: none;\n touch-action: none;\n}\n\n/* Room below the picture for a layer that steps out of the frame (decision 13). */\n.wx-banner-layers__canvas.is-overflow {\n padding-bottom: 24%;\n background-image: repeating-linear-gradient(\n 45deg,\n transparent 0 8px,\n color-mix(in srgb, var(--wx-text-muted) 12%, transparent) 8px 9px\n );\n}\n\n.wx-banner-layers__canvas:focus-visible {\n outline: 2px solid var(--wx-color-primary);\n}\n\n.wx-banner-layers__frame {\n overflow: visible;\n}\n\n.wx-banner-layers__frame :deep(.webx-banner__stage) {\n pointer-events: auto;\n}\n\n/* The site's motion is the site's: the canvas stands still, so a layer is where it ends up. */\n.wx-banner-layers__frame :deep(.webx-banner__layer),\n.wx-banner-layers__frame :deep(.webx-banner__inner) {\n animation: none !important;\n cursor: move;\n pointer-events: auto;\n}\n\n.wx-banner-layers__words {\n white-space: pre-line;\n}\n\n.wx-banner-layers__frame :deep(.webx-banner__layer.is-selected) {\n outline: 2px solid var(--wx-color-primary);\n outline-offset: 1px;\n}\n\n.wx-banner-layers__frame :deep(.webx-banner__layer:hover:not(.is-selected)) {\n outline: 1px dashed var(--wx-color-primary);\n}\n\n.wx-banner-layers__frame :deep(.webx-banner__layer.is-outside) {\n opacity: 0.5;\n}\n\n.wx-banner-layers__handle {\n position: absolute;\n width: 10px;\n height: 10px;\n border: 2px solid var(--wx-color-primary);\n border-radius: 2px;\n background: var(--wx-bg-surface);\n translate: 50% -50%;\n}\n\n.wx-banner-layers__handle--width {\n top: 50%;\n right: 0;\n cursor: ew-resize;\n}\n\n.wx-banner-layers__handle--scale {\n right: 0;\n bottom: 0;\n translate: 50% 50%;\n cursor: nwse-resize;\n}\n\n/* What the frame of the place crops away is shaded: the site never shows it. */\n.wx-banner-layers__crop {\n position: absolute;\n z-index: 1000;\n box-shadow: 0 0 0 100vmax rgb(0 0 0 / 0.45);\n outline: 1px dashed rgb(255 255 255 / 0.8);\n pointer-events: none;\n}\n\n.wx-banner-layers__guide {\n position: absolute;\n z-index: 1001;\n background: var(--wx-color-primary);\n pointer-events: none;\n}\n\n.wx-banner-layers__guide--x {\n top: 0;\n bottom: 0;\n width: 1px;\n}\n\n.wx-banner-layers__guide--y {\n left: 0;\n right: 0;\n height: 1px;\n}\n\n.wx-banner-layers__empty {\n display: grid;\n place-items: center;\n height: 100%;\n color: var(--wx-text-muted);\n}\n\n.wx-banner-layers__row {\n display: flex;\n align-items: center;\n gap: var(--wx-space-8);\n width: 100%;\n min-width: 0;\n padding: 0;\n border: 0;\n background: none;\n color: inherit;\n font: inherit;\n text-align: start;\n cursor: pointer;\n}\n\n.wx-banner-layers__row.is-selected {\n color: var(--wx-color-primary);\n font-weight: 600;\n}\n\n.wx-banner-layers__name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.wx-banner-layers__warn {\n color: var(--wx-color-warning);\n}\n</style>\n","import type { AdminModule } from '@webx-ui/module-admin'\nimport BannerEditorPage from './BannerEditorPage.vue'\nimport BannersPage from './BannersPage.vue'\nimport LayersField from './LayersField.vue'\n\nexport interface BannersOptions {\n /** Where the banners live inside the panel. */\n path?: string\n}\n\n/**\n * Banners as a section of the panel (§5.4): one module and one entry, without a group.\n *\n * Two screens. The places and the banners of one of them are a list and its detail, the way the\n * menus are; a banner is a page of its own, because its form is long — three pictures, the words\n * and up to three buttons — and a pane beside a list is no room for it. A section whose server\n * half is not installed never appears — the entry is built from the manifest.\n */\nexport function banners(options: BannersOptions = {}): AdminModule {\n const path = options.path ?? '/banners'\n\n return {\n id: 'banners',\n path,\n routes: [\n // The open place is in the address (`?place=`), so a link to it is a link to its banners.\n { path, name: 'webx.banners', component: BannersPage, props: { base: path } },\n // A banner not written yet: the place it goes to rides in the query (`?place=hero`).\n {\n path: `${path}/new`,\n name: 'webx.banners.new',\n component: BannerEditorPage,\n props: { base: path },\n },\n {\n path: `${path}/:id(\\\\d+)`,\n name: 'webx.banners.edit',\n component: BannerEditorPage,\n props: { base: path },\n },\n ],\n /*\n * The one node of `banners.form` only this module can draw: the layers, on the banner's\n * picture. A field like any other — its value is the list of layers.\n */\n types: {\n 'wx-banner-layers': { component: LayersField, kind: 'field', wide: true },\n },\n }\n}\n"],"names":["createBannersApi","admin","base","place","key","data","body","input","title","trashed","id","values","ids","provideBannerEditor","context","provide","useBannerEditor","inject","FRAME_LIMITS","ORIGINS","ORIGIN_SHIFT","ENTRANCES","LOOPS","FIELDS","frame","over","style","motion","clone","value","layerId","taken","used","newLayer","type","locale","defaultLayers","defaultLocale","next","layer","made","layerWords","text","order","fields","map","code","one","unknownFields","match","name","cssColor","color","token","round","layerVars","z","vars","desktop","mobile","prefix","ox","oy","background","layerClasses","classes","ratioOf","fallback","ratio","cropBox","image","frameRatio","width","height","clampFrame","min","max","bannersMessages","seeded","useBannersMessages","i18n","useAdmin","props","__props","api","route","useRoute","router","useRouter","locales","useLocales","t","useTranslate","message","useErrorText","computed","raw","banner","ref","snapshot","placeSaved","places","loading","saving","errors","canManage","current","dirty","state","written","localizedValue","back","section","module","placeRow","screenPatch","withDefaultLayers","model","placeOptions","placeModel","blank","take","detail","opened","load","list","asked","fresh","error","toast","save","remove","record","confirm","leaveGuard","event","onKeydown","useBodyKeys","watch","onMounted","onBeforeUnmount","onBeforeRouteLeave","actions","_createElementBlock","_Fragment","_createVNode","_unref","WxSkeleton","WxCard","WxScreenHead","WxBadge","WxFormItem","WxSelect","$event","WxScreen","_createBlock","WxActionBar","WxSaveState","WxButton","open","resolve","dismiss","useModal","editing","initial","first","field","useTemplateRef","nextTick","words","errorOf","found","submit","WxDialog","WxSpace","_createElementVNode","WxInput","emit","__emit","rows","view","inBin","views","sortable","layout","edit","row","add","reorder","toggle","restore","actionsFor","_openBlock","_hoisted_1","WxTooltip","WxIcon","_hoisted_3","_toDisplayString","_hoisted_4","WxSegmented","WxEmpty","WxSortableList","_normalizeClass","item","_withCtx","_resolveDynamicComponent","_hoisted_5","_hoisted_7","_hoisted_8","_hoisted_9","WxRowMenu","WxAlert","detailInline","createModal","PlaceDialog","chosen","choose","query","rename","rest","WxListScreen","WxListDetail","_cache","_renderList","WxText","_createTextVNode","WxIndicator","inline","PlacePane","ownPhone","change","mutate","setStyle","hasWords","typos","typoList","fallsBackTo","active","insert","link","choice","get","set","tag","variant","font","weight","align","entrance","loop","numbers","steps","setNumber","setOrigin","origin","fontOptions","weightOptions","alignOptions","entranceOptions","loopOptions","colorOf","pickColor","hex","_hoisted_2","WxTextarea","WxMediaField","WxLinkField","_hoisted_6","WxInputNumber","WxSwitch","_hoisted_10","_hoisted_11","_hoisted_12","swatch","_normalizeStyle","WxColorPicker","_hoisted_14","_hoisted_15","SLOP","SNAP","editor","media","createMediaApi","layers","selectedId","selected","past","future","remember","commit","undo","last","redo","replace","frameOf","which","setFrame","patch","lastEdit","now","merge","duplicate","index","source","copy","toggleHidden","hasButtonsLayer","listModel","pictures","reactive","pathOf","path","file","desktopPath","mobilePath","paths","imageRatio","crop","frameStyle","ownFields","own","wordsOf","bannerButtons","button","tagOf","labelOf","icons","outside","box","left","right","top","bottom","x","originShift","stage","canvas","guides","snapTargets","axis","skip","targets","snap","size","unit","free","edges","best","target","edge","delta","select","press","mode","from","start","moving","move","dx","dy","shift","factor","up","y","pressCanvas","mod","step","by","theme","themeStyle","css","viewOptions","viewModel","_withModifiers","at","_hoisted_13","guide","_hoisted_17","LayerInspector","banners","options","BannersPage","BannerEditorPage","LayersField"],"mappings":"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"}
|
package/dist/style.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.wx-banner-editor[data-v-b6bd4d3f]{display:flex;flex-direction:column;gap:var(--wx-gap, var(--wx-space-16));container-type:inline-size}.wx-banner-editor__ghost[data-v-b6bd4d3f]{max-width:420px}.wx-banner-editor__place[data-v-b6bd4d3f] .wx-form-item{max-width:420px;margin-bottom:0}.wx-banner-place[data-v-23c95ae5]{display:flex;flex-direction:column;gap:var(--wx-space-12);min-width:0;padding:var(--wx-gap, var(--wx-space-16))}.wx-banner-place__lock[data-v-23c95ae5]{display:inline-flex;color:var(--wx-text-muted)}.wx-banner-place__usage[data-v-23c95ae5]{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--wx-space-6);margin:0;color:var(--wx-text-muted);font-size:var(--wx-font-size-sm)}.wx-banner-place__usage code[data-v-23c95ae5]{padding:0 var(--wx-space-4);border-radius:var(--wx-radius-xs);background:var(--wx-bg-subtle);color:var(--wx-text-default);font-family:var(--wx-font-family-mono);user-select:all}.wx-banner-place__views[data-v-23c95ae5]{align-self:flex-start}.wx-banner-place__list.is-loading[data-v-23c95ae5]{opacity:.6}.wx-banner-place__list[data-v-23c95ae5] .wx-sortable-list__row{gap:var(--wx-space-4);padding-inline:var(--wx-space-4);border-radius:var(--wx-radius-sm)}.wx-banner-place__list[data-v-23c95ae5] .wx-sortable-list__row:hover{background:var(--wx-bg-subtle)}.wx-banner-row[data-v-23c95ae5]{display:flex;align-items:center;gap:var(--wx-space-12);flex:1 1 auto;width:100%;min-width:0;padding-block:var(--wx-space-8);color:inherit;text-decoration:none}.wx-banner-row.is-off .wx-banner-row__thumb[data-v-23c95ae5],.wx-banner-row.is-off .wx-banner-row__title[data-v-23c95ae5]{opacity:.55}.wx-banner-row__thumb[data-v-23c95ae5]{display:flex;flex:none;align-items:center;justify-content:center;width:80px;aspect-ratio:16 / 9;overflow:hidden;border-radius:var(--wx-radius-xs);background:var(--wx-bg-fill);color:var(--wx-text-muted)}.wx-banner-row__thumb img[data-v-23c95ae5]{width:100%;height:100%;min-width:0;object-fit:cover}.wx-banner-row__body[data-v-23c95ae5]{display:flex;flex:1 1 auto;flex-direction:column;gap:var(--wx-space-4);min-width:0}.wx-banner-row__title[data-v-23c95ae5]{min-width:0;overflow:hidden;font-weight:var(--wx-font-weight-medium);text-overflow:ellipsis;white-space:nowrap}a.wx-banner-row:hover .wx-banner-row__title[data-v-23c95ae5]{color:var(--wx-color-primary)}.wx-banner-row__facts[data-v-23c95ae5]{display:flex;flex-wrap:wrap;gap:var(--wx-space-4)}.wx-banner-place__note[data-v-23c95ae5]{margin-top:var(--wx-space-4)}.wx-banners>.wx-card__body{display:flex;flex-direction:column;overflow:clip;border-radius:inherit}.wx-banners__loading[data-v-615232f0]{padding:var(--wx-space-16)}.wx-banners__list[data-v-615232f0]{margin:0;padding:var(--wx-space-8);list-style:none}.wx-banners__row[data-v-615232f0]{display:flex;align-items:center;gap:var(--wx-space-4);margin:0;padding-inline:var(--wx-space-8);border-radius:var(--wx-radius-sm)}.wx-banners__row[data-v-615232f0]:has(.is-current){background:var(--wx-bg-subtle)}.wx-banners__open[data-v-615232f0]{display:flex;flex-direction:column;align-items:flex-start;gap:var(--wx-space-2);flex:1 1 auto;min-width:0;padding-block:var(--wx-space-8);padding-inline:0;border:0;background:none;font:inherit;color:inherit;text-align:start;cursor:pointer}.wx-banners__open.is-current[data-v-615232f0]{color:var(--wx-color-primary)}.wx-banners__name[data-v-615232f0]{display:flex;align-items:center;gap:var(--wx-space-6);min-width:0;max-width:100%}.wx-banners__lock[data-v-615232f0]{flex:none;color:var(--wx-text-muted)}.wx-banners__count[data-v-615232f0]{flex:0 0 auto}.wx-banner-layer-inspector[data-v-32ff51cd]{display:flex;flex-direction:column;gap:var(--wx-space-12);min-width:0}.wx-banner-layer-inspector__head[data-v-32ff51cd]{display:flex;gap:var(--wx-space-8)}.wx-banner-layer-inspector__fields[data-v-32ff51cd]{display:flex;gap:var(--wx-space-4);margin-top:calc(-1 * var(--wx-space-8))}.wx-banner-layer-inspector__group[data-v-32ff51cd]{display:flex;flex-direction:column;gap:var(--wx-space-8);padding-top:var(--wx-space-12);border-top:1px solid var(--wx-border-default)}.wx-banner-layer-inspector__title[data-v-32ff51cd]{display:flex;align-items:center;justify-content:space-between;gap:var(--wx-space-8)}.wx-banner-layer-inspector__grid[data-v-32ff51cd]{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 var(--wx-space-8)}.wx-banner-layer-inspector__grid[data-v-32ff51cd] .wx-form-item,.wx-banner-layer-inspector[data-v-32ff51cd] .wx-form-item{margin-bottom:var(--wx-space-4)}.wx-banner-layer-inspector__switches[data-v-32ff51cd]{display:flex;flex-wrap:wrap;gap:var(--wx-space-12)}.wx-banner-layer-inspector__origins[data-v-32ff51cd]{display:grid;grid-template-columns:repeat(3,18px);gap:var(--wx-space-4)}.wx-banner-layer-inspector__origin[data-v-32ff51cd]{width:18px;height:18px;padding:0;border:1px solid var(--wx-border-default);border-radius:var(--wx-radius-xs);background:var(--wx-bg-surface);cursor:pointer}.wx-banner-layer-inspector__origin.is-active[data-v-32ff51cd]{border-color:var(--wx-color-primary);background:var(--wx-color-primary)}.wx-banner-layer-inspector__color[data-v-32ff51cd]{display:flex;flex-wrap:wrap;align-items:center;gap:var(--wx-space-4)}.wx-banner-layer-inspector__swatch[data-v-32ff51cd]{width:22px;height:22px;padding:0;border:1px solid var(--wx-border-default);border-radius:var(--wx-radius-full);cursor:pointer}.wx-banner-layer-inspector__swatch.is-active[data-v-32ff51cd]{outline:2px solid var(--wx-color-primary);outline-offset:1px}.webx-banners{position:relative;min-width:0}.webx-banners--overflow{z-index:1;overflow-x:clip}.webx-banners--overflow .webx-slider__viewport{overflow:visible}.webx-banners__strip{display:flex;overflow-x:auto;scroll-snap-type:x mandatory;overscroll-behavior-x:contain}.webx-banners__strip>.webx-banners__item{flex:0 0 100%;scroll-snap-align:start}.webx-banners__strip.is-ready{scrollbar-width:none}.webx-banners__controls{display:flex;align-items:center;justify-content:center;gap:.75rem;padding:.75rem}.webx-banners__controls[hidden]{display:none}.webx-banners__arrow{width:2.25rem;height:2.25rem;padding:0;border:1px solid currentColor;border-radius:50%;background:none;color:inherit;font:inherit;font-size:1.25rem;line-height:1;cursor:pointer}.webx-banners__dots{display:flex;gap:.4rem}.webx-banners__dot{width:.6rem;height:.6rem;padding:0;border:0;border-radius:50%;background:currentColor;opacity:.3;cursor:pointer}.webx-banners__dot[aria-current=true]{opacity:1}.webx-banner{position:relative;aspect-ratio:var(--ratio, 16 / 6);container-type:size;overflow:hidden}.webx-banners--overflow .webx-banner{overflow:visible}.webx-banner__picture{position:absolute;inset:0;overflow:hidden}.webx-banner__picture>picture{display:contents}.webx-banner__image,.webx-banner__video{display:block;width:100%;height:100%;object-fit:cover}.webx-banner__video{position:absolute;inset:0}.webx-banner__stage{position:absolute;top:50%;left:50%;width:max(100cqw,100cqh * var(--img, 2.6667));aspect-ratio:var(--img, 2.6667);translate:-50% -50%;container-type:inline-size;pointer-events:none}.webx-banner__layer{position:absolute;top:calc(var(--y) * 1cqw);left:calc(var(--x) * 1cqw);display:var(--display, block);box-sizing:border-box;width:calc(var(--w) * 1cqw);margin:0;padding:calc(var(--pad) * 1cqw);border-radius:calc(var(--radius) * 1cqw);background:var(--background, transparent);color:var(--color, inherit);font-family:var(--font, inherit);font-size:calc(var(--size) * 1cqw);font-weight:var(--weight, 400);line-height:var(--leading, 1.2);letter-spacing:calc(var(--tracking) * 1em);text-align:var(--align, start);overflow-wrap:break-word;opacity:var(--opacity, 1);translate:var(--ox) var(--oy);rotate:calc(var(--rotate) * 1deg);pointer-events:auto}.webx-banner__layer--image{padding:0}.webx-banner__layer--image:has(img[alt=""]){pointer-events:none}.webx-banner__layer--image .webx-banner__inner{display:block;width:100%;height:auto}.webx-banner__layer.is-italic{font-style:italic}.webx-banner__layer.is-uppercase{text-transform:uppercase}.webx-banner__layer.is-shadow{text-shadow:0 .05em .3em rgb(0 0 0 / .45)}.webx-banner__layer.has-background{width:fit-content;max-width:calc(var(--w) * 1cqw)}.webx-banner__buttons{display:flex;flex-wrap:wrap;justify-content:var(--align, start);gap:calc(var(--gap) * 1cqw)}.webx-banner__button{display:inline-block;padding:.6em 1.4em;border-radius:var(--site-radius-control, .4em);background:var(--site-color-accent, #111);color:var(--site-color-accent-contrast, #fff);font:inherit;text-decoration:none;white-space:nowrap}.webx-banner__button--secondary{background:var(--site-color-surface, #fff);color:var(--site-color-text, #111)}.webx-banner__button--link{padding-inline:0;background:none;color:inherit;text-decoration:underline}.webx-banner__content{position:absolute;inset:auto 0 0;display:grid;gap:.5em;padding:clamp(1rem,4cqw,3rem);color:#fff;text-shadow:0 1px 8px rgb(0 0 0 / .5)}.webx-banner__title,.webx-banner__text{margin:0}.webx-banner.is-mobile{aspect-ratio:var(--ratio-mobile, 4 / 5)}.webx-banner.is-mobile .webx-banner__stage{width:max(100cqw,100cqh * var(--img-m, var(--img, .8)));aspect-ratio:var(--img-m, var(--img, .8))}.webx-banner.is-mobile .webx-banner__layer{top:calc(var(--m-y) * 1cqw);left:calc(var(--m-x) * 1cqw);display:var(--m-display, block);width:calc(var(--m-w) * 1cqw);padding:calc(var(--m-pad) * 1cqw);border-radius:calc(var(--m-radius) * 1cqw);font-size:calc(var(--m-size) * 1cqw);letter-spacing:calc(var(--m-tracking) * 1em);translate:var(--m-ox) var(--m-oy);rotate:calc(var(--m-rotate) * 1deg)}.webx-banner.is-mobile .webx-banner__layer--image{padding:0}.webx-banner.is-mobile .webx-banner__layer.has-background{max-width:calc(var(--m-w) * 1cqw)}.webx-banner.is-mobile .webx-banner__buttons{gap:calc(var(--m-gap) * 1cqw)}.webx-banner__layer[class*=is-in-]{animation-duration:var(--duration, .6s);animation-delay:var(--delay, 0ms);animation-fill-mode:both;animation-timing-function:cubic-bezier(.2,.7,.2,1)}.webx-banner__layer.is-in-fade{animation-name:webx-banner-fade}.webx-banner__layer.is-in-rise{animation-name:webx-banner-rise}.webx-banner__layer.is-in-slide-start{animation-name:webx-banner-slide-start}.webx-banner__layer.is-in-slide-end{animation-name:webx-banner-slide-end}.webx-banner__layer.is-in-zoom{animation-name:webx-banner-zoom}.webx-slider__track.is-ready .webx-slider__slide:not(.is-active) .webx-banner__layer[class*=is-in-],.webx-banners__strip.is-ready>.webx-banners__item:not(.is-active) .webx-banner__layer[class*=is-in-]{animation-name:none}.webx-banner__layer.is-loop-spin>.webx-banner__inner{animation:webx-banner-spin 24s linear infinite}.webx-banner__layer.is-loop-float>.webx-banner__inner{animation:webx-banner-float 3.2s ease-in-out infinite alternate}.webx-banner__layer>.webx-banner__inner{display:block}@keyframes webx-banner-fade{0%{opacity:0}}@keyframes webx-banner-rise{0%{opacity:0;transform:translateY(.8em)}}@keyframes webx-banner-slide-start{0%{opacity:0;transform:translate(-1.5em)}}@keyframes webx-banner-slide-end{0%{opacity:0;transform:translate(1.5em)}}@keyframes webx-banner-zoom{0%{opacity:0;transform:scale(.85)}}@keyframes webx-banner-spin{to{transform:rotate(1turn)}}@keyframes webx-banner-float{to{transform:translateY(-6%)}}@media(prefers-reduced-motion:reduce){.webx-banner__layer,.webx-banner__layer>.webx-banner__inner{animation:none!important}}.wx-banner-layers[data-v-540c304c]{display:flex;flex-direction:column;gap:var(--wx-space-12);container-type:inline-size;min-width:0}.wx-banner-layers__bar[data-v-540c304c]{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--wx-space-8)}.wx-banner-layers__tools[data-v-540c304c]{display:flex;flex-wrap:wrap;gap:var(--wx-space-4)}.wx-banner-layers__body[data-v-540c304c]{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--wx-space-16)}@container (min-width: 900px){.wx-banner-layers__body[data-v-540c304c]{grid-template-columns:minmax(0,1fr) 320px;align-items:start}}.wx-banner-layers__main[data-v-540c304c]{display:flex;flex-direction:column;gap:var(--wx-space-8);min-width:0}.wx-banner-layers__canvas[data-v-540c304c]{position:relative;isolation:isolate;overflow:hidden;border-radius:var(--wx-radius-sm);background:var(--wx-bg-muted, var(--wx-bg-surface));outline-offset:2px;user-select:none;touch-action:none}.wx-banner-layers__canvas.is-overflow[data-v-540c304c]{padding-bottom:24%;background-image:repeating-linear-gradient(45deg,transparent 0 8px,color-mix(in srgb,var(--wx-text-muted) 12%,transparent) 8px 9px)}.wx-banner-layers__canvas[data-v-540c304c]:focus-visible{outline:2px solid var(--wx-color-primary)}.wx-banner-layers__frame[data-v-540c304c]{overflow:visible}.wx-banner-layers__frame[data-v-540c304c] .webx-banner__stage{pointer-events:auto}.wx-banner-layers__frame[data-v-540c304c] .webx-banner__layer,.wx-banner-layers__frame[data-v-540c304c] .webx-banner__inner{animation:none!important;cursor:move;pointer-events:auto}.wx-banner-layers__words[data-v-540c304c]{white-space:pre-line}.wx-banner-layers__frame[data-v-540c304c] .webx-banner__layer.is-selected{outline:2px solid var(--wx-color-primary);outline-offset:1px}.wx-banner-layers__frame[data-v-540c304c] .webx-banner__layer:hover:not(.is-selected){outline:1px dashed var(--wx-color-primary)}.wx-banner-layers__frame[data-v-540c304c] .webx-banner__layer.is-outside{opacity:.5}.wx-banner-layers__handle[data-v-540c304c]{position:absolute;width:10px;height:10px;border:2px solid var(--wx-color-primary);border-radius:2px;background:var(--wx-bg-surface);translate:50% -50%}.wx-banner-layers__handle--width[data-v-540c304c]{top:50%;right:0;cursor:ew-resize}.wx-banner-layers__handle--scale[data-v-540c304c]{right:0;bottom:0;translate:50% 50%;cursor:nwse-resize}.wx-banner-layers__crop[data-v-540c304c]{position:absolute;z-index:1000;box-shadow:0 0 0 100vmax #00000073;outline:1px dashed rgb(255 255 255 / .8);pointer-events:none}.wx-banner-layers__guide[data-v-540c304c]{position:absolute;z-index:1001;background:var(--wx-color-primary);pointer-events:none}.wx-banner-layers__guide--x[data-v-540c304c]{top:0;bottom:0;width:1px}.wx-banner-layers__guide--y[data-v-540c304c]{left:0;right:0;height:1px}.wx-banner-layers__empty[data-v-540c304c]{display:grid;place-items:center;height:100%;color:var(--wx-text-muted)}.wx-banner-layers__row[data-v-540c304c]{display:flex;align-items:center;gap:var(--wx-space-8);width:100%;min-width:0;padding:0;border:0;background:none;color:inherit;font:inherit;text-align:start;cursor:pointer}.wx-banner-layers__row.is-selected[data-v-540c304c]{color:var(--wx-color-primary);font-weight:600}.wx-banner-layers__name[data-v-540c304c]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wx-banner-layers__warn[data-v-540c304c]{color:var(--wx-color-warning)}
|
|
1
|
+
.wx-banner-editor[data-v-b6bd4d3f]{display:flex;flex-direction:column;gap:var(--wx-gap, var(--wx-space-16));container-type:inline-size}.wx-banner-editor__ghost[data-v-b6bd4d3f]{max-width:420px}.wx-banner-editor__place[data-v-b6bd4d3f] .wx-form-item{max-width:420px;margin-bottom:0}.wx-banner-place[data-v-48d645bf]{display:flex;flex-direction:column;gap:var(--wx-space-12);min-width:0;padding:var(--wx-gap, var(--wx-space-16))}.wx-banner-place__lock[data-v-48d645bf]{display:inline-flex;color:var(--wx-text-muted)}.wx-banner-place__usage[data-v-48d645bf]{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--wx-space-6);margin:0;color:var(--wx-text-muted);font-size:var(--wx-font-size-sm)}.wx-banner-place__usage code[data-v-48d645bf]{padding:0 var(--wx-space-4);border-radius:var(--wx-radius-xs);background:var(--wx-bg-subtle);color:var(--wx-text-default);font-family:var(--wx-font-family-mono);user-select:all}.wx-banner-place__views[data-v-48d645bf]{align-self:flex-start}.wx-banner-place__list.is-loading[data-v-48d645bf]{opacity:.6}.wx-banner-place__list[data-v-48d645bf] .wx-sortable-list__row{gap:var(--wx-space-4);padding-inline:var(--wx-space-4)}.wx-banner-row[data-v-48d645bf]{display:flex;align-items:center;gap:var(--wx-space-12);flex:1 1 auto;width:100%;min-width:0;padding-block:var(--wx-space-8);color:inherit;text-decoration:none}.wx-banner-row.is-off .wx-banner-row__thumb[data-v-48d645bf],.wx-banner-row.is-off .wx-banner-row__title[data-v-48d645bf]{opacity:.55}.wx-banner-row__thumb[data-v-48d645bf]{display:flex;flex:none;align-items:center;justify-content:center;width:80px;aspect-ratio:16 / 9;overflow:hidden;border-radius:var(--wx-radius-xs);background:var(--wx-bg-fill);color:var(--wx-text-muted)}.wx-banner-row__thumb img[data-v-48d645bf]{width:100%;height:100%;min-width:0;object-fit:cover}.wx-banner-row__body[data-v-48d645bf]{display:flex;flex:1 1 auto;flex-direction:column;gap:var(--wx-space-4);min-width:0}.wx-banner-row__title[data-v-48d645bf]{min-width:0;overflow:hidden;font-weight:var(--wx-font-weight-medium);text-overflow:ellipsis;white-space:nowrap}a.wx-banner-row:hover .wx-banner-row__title[data-v-48d645bf]{color:var(--wx-color-primary)}.wx-banner-row__facts[data-v-48d645bf]{display:flex;flex-wrap:wrap;gap:var(--wx-space-4)}.wx-banner-place__note[data-v-48d645bf]{margin-top:var(--wx-space-4)}.wx-banners>.wx-card__body{display:flex;flex-direction:column;overflow:clip;border-radius:inherit}.wx-banners__loading[data-v-615232f0]{padding:var(--wx-space-16)}.wx-banners__list[data-v-615232f0]{margin:0;padding:var(--wx-space-8);list-style:none}.wx-banners__row[data-v-615232f0]{display:flex;align-items:center;gap:var(--wx-space-4);margin:0;padding-inline:var(--wx-space-8);border-radius:var(--wx-radius-sm)}.wx-banners__row[data-v-615232f0]:has(.is-current){background:var(--wx-bg-subtle)}.wx-banners__open[data-v-615232f0]{display:flex;flex-direction:column;align-items:flex-start;gap:var(--wx-space-2);flex:1 1 auto;min-width:0;padding-block:var(--wx-space-8);padding-inline:0;border:0;background:none;font:inherit;color:inherit;text-align:start;cursor:pointer}.wx-banners__open.is-current[data-v-615232f0]{color:var(--wx-color-primary)}.wx-banners__name[data-v-615232f0]{display:flex;align-items:center;gap:var(--wx-space-6);min-width:0;max-width:100%}.wx-banners__lock[data-v-615232f0]{flex:none;color:var(--wx-text-muted)}.wx-banners__count[data-v-615232f0]{flex:0 0 auto}.wx-banner-layer-inspector[data-v-32ff51cd]{display:flex;flex-direction:column;gap:var(--wx-space-12);min-width:0}.wx-banner-layer-inspector__head[data-v-32ff51cd]{display:flex;gap:var(--wx-space-8)}.wx-banner-layer-inspector__fields[data-v-32ff51cd]{display:flex;gap:var(--wx-space-4);margin-top:calc(-1 * var(--wx-space-8))}.wx-banner-layer-inspector__group[data-v-32ff51cd]{display:flex;flex-direction:column;gap:var(--wx-space-8);padding-top:var(--wx-space-12);border-top:1px solid var(--wx-border-default)}.wx-banner-layer-inspector__title[data-v-32ff51cd]{display:flex;align-items:center;justify-content:space-between;gap:var(--wx-space-8)}.wx-banner-layer-inspector__grid[data-v-32ff51cd]{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 var(--wx-space-8)}.wx-banner-layer-inspector__grid[data-v-32ff51cd] .wx-form-item,.wx-banner-layer-inspector[data-v-32ff51cd] .wx-form-item{margin-bottom:var(--wx-space-4)}.wx-banner-layer-inspector__switches[data-v-32ff51cd]{display:flex;flex-wrap:wrap;gap:var(--wx-space-12)}.wx-banner-layer-inspector__origins[data-v-32ff51cd]{display:grid;grid-template-columns:repeat(3,18px);gap:var(--wx-space-4)}.wx-banner-layer-inspector__origin[data-v-32ff51cd]{width:18px;height:18px;padding:0;border:1px solid var(--wx-border-default);border-radius:var(--wx-radius-xs);background:var(--wx-bg-surface);cursor:pointer}.wx-banner-layer-inspector__origin.is-active[data-v-32ff51cd]{border-color:var(--wx-color-primary);background:var(--wx-color-primary)}.wx-banner-layer-inspector__color[data-v-32ff51cd]{display:flex;flex-wrap:wrap;align-items:center;gap:var(--wx-space-4)}.wx-banner-layer-inspector__swatch[data-v-32ff51cd]{width:22px;height:22px;padding:0;border:1px solid var(--wx-border-default);border-radius:var(--wx-radius-full);cursor:pointer}.wx-banner-layer-inspector__swatch.is-active[data-v-32ff51cd]{outline:2px solid var(--wx-color-primary);outline-offset:1px}.webx-banners{position:relative;min-width:0}.webx-banners--overflow{z-index:1;overflow-x:clip}.webx-banners--overflow .webx-slider__viewport{overflow:visible}.webx-banners__strip{display:flex;overflow-x:auto;scroll-snap-type:x mandatory;overscroll-behavior-x:contain}.webx-banners__strip>.webx-banners__item{flex:0 0 100%;scroll-snap-align:start}.webx-banners__strip.is-ready{scrollbar-width:none}.webx-banners__controls{display:flex;align-items:center;justify-content:center;gap:.75rem;padding:.75rem}.webx-banners__controls[hidden]{display:none}.webx-banners__arrow{width:2.25rem;height:2.25rem;padding:0;border:1px solid currentColor;border-radius:50%;background:none;color:inherit;font:inherit;font-size:1.25rem;line-height:1;cursor:pointer}.webx-banners__dots{display:flex;gap:.4rem}.webx-banners__dot{width:.6rem;height:.6rem;padding:0;border:0;border-radius:50%;background:currentColor;opacity:.3;cursor:pointer}.webx-banners__dot[aria-current=true]{opacity:1}.webx-banner{position:relative;aspect-ratio:var(--ratio, 16 / 6);container-type:size;overflow:hidden}.webx-banners--overflow .webx-banner{overflow:visible}.webx-banner__picture{position:absolute;inset:0;overflow:hidden}.webx-banner__picture>picture{display:contents}.webx-banner__image,.webx-banner__video{display:block;width:100%;height:100%;object-fit:cover}.webx-banner__video{position:absolute;inset:0}.webx-banner__stage{position:absolute;top:50%;left:50%;width:max(100cqw,100cqh * var(--img, 2.6667));aspect-ratio:var(--img, 2.6667);translate:-50% -50%;container-type:inline-size;pointer-events:none}.webx-banner__layer{position:absolute;top:calc(var(--y) * 1cqw);left:calc(var(--x) * 1cqw);display:var(--display, block);box-sizing:border-box;width:calc(var(--w) * 1cqw);margin:0;padding:calc(var(--pad) * 1cqw);border-radius:calc(var(--radius) * 1cqw);background:var(--background, transparent);color:var(--color, inherit);font-family:var(--font, inherit);font-size:calc(var(--size) * 1cqw);font-weight:var(--weight, 400);line-height:var(--leading, 1.2);letter-spacing:calc(var(--tracking) * 1em);text-align:var(--align, start);overflow-wrap:break-word;opacity:var(--opacity, 1);translate:var(--ox) var(--oy);rotate:calc(var(--rotate) * 1deg);pointer-events:auto}.webx-banner__layer--image{padding:0}.webx-banner__layer--image:has(img[alt=""]){pointer-events:none}.webx-banner__layer--image .webx-banner__inner{display:block;width:100%;height:auto}.webx-banner__layer.is-italic{font-style:italic}.webx-banner__layer.is-uppercase{text-transform:uppercase}.webx-banner__layer.is-shadow{text-shadow:0 .05em .3em rgb(0 0 0 / .45)}.webx-banner__layer.has-background{width:fit-content;max-width:calc(var(--w) * 1cqw)}.webx-banner__buttons{display:flex;flex-wrap:wrap;justify-content:var(--align, start);gap:calc(var(--gap) * 1cqw)}.webx-banner__button{display:inline-block;padding:.6em 1.4em;border-radius:var(--site-radius-control, .4em);background:var(--site-color-accent, #111);color:var(--site-color-accent-contrast, #fff);font:inherit;text-decoration:none;white-space:nowrap}.webx-banner__button--secondary{background:var(--site-color-surface, #fff);color:var(--site-color-text, #111)}.webx-banner__button--link{padding-inline:0;background:none;color:inherit;text-decoration:underline}.webx-banner__content{position:absolute;inset:auto 0 0;display:grid;gap:.5em;padding:clamp(1rem,4cqw,3rem);color:#fff;text-shadow:0 1px 8px rgb(0 0 0 / .5)}.webx-banner__title,.webx-banner__text{margin:0}.webx-banner.is-mobile{aspect-ratio:var(--ratio-mobile, 4 / 5)}.webx-banner.is-mobile .webx-banner__stage{width:max(100cqw,100cqh * var(--img-m, var(--img, .8)));aspect-ratio:var(--img-m, var(--img, .8))}.webx-banner.is-mobile .webx-banner__layer{top:calc(var(--m-y) * 1cqw);left:calc(var(--m-x) * 1cqw);display:var(--m-display, block);width:calc(var(--m-w) * 1cqw);padding:calc(var(--m-pad) * 1cqw);border-radius:calc(var(--m-radius) * 1cqw);font-size:calc(var(--m-size) * 1cqw);letter-spacing:calc(var(--m-tracking) * 1em);translate:var(--m-ox) var(--m-oy);rotate:calc(var(--m-rotate) * 1deg)}.webx-banner.is-mobile .webx-banner__layer--image{padding:0}.webx-banner.is-mobile .webx-banner__layer.has-background{max-width:calc(var(--m-w) * 1cqw)}.webx-banner.is-mobile .webx-banner__buttons{gap:calc(var(--m-gap) * 1cqw)}.webx-banner__layer[class*=is-in-]{animation-duration:var(--duration, .6s);animation-delay:var(--delay, 0ms);animation-fill-mode:both;animation-timing-function:cubic-bezier(.2,.7,.2,1)}.webx-banner__layer.is-in-fade{animation-name:webx-banner-fade}.webx-banner__layer.is-in-rise{animation-name:webx-banner-rise}.webx-banner__layer.is-in-slide-start{animation-name:webx-banner-slide-start}.webx-banner__layer.is-in-slide-end{animation-name:webx-banner-slide-end}.webx-banner__layer.is-in-zoom{animation-name:webx-banner-zoom}.webx-slider__track.is-ready .webx-slider__slide:not(.is-active) .webx-banner__layer[class*=is-in-],.webx-banners__strip.is-ready>.webx-banners__item:not(.is-active) .webx-banner__layer[class*=is-in-]{animation-name:none}.webx-banner__layer.is-loop-spin>.webx-banner__inner{animation:webx-banner-spin 24s linear infinite}.webx-banner__layer.is-loop-float>.webx-banner__inner{animation:webx-banner-float 3.2s ease-in-out infinite alternate}.webx-banner__layer>.webx-banner__inner{display:block}@keyframes webx-banner-fade{0%{opacity:0}}@keyframes webx-banner-rise{0%{opacity:0;transform:translateY(.8em)}}@keyframes webx-banner-slide-start{0%{opacity:0;transform:translate(-1.5em)}}@keyframes webx-banner-slide-end{0%{opacity:0;transform:translate(1.5em)}}@keyframes webx-banner-zoom{0%{opacity:0;transform:scale(.85)}}@keyframes webx-banner-spin{to{transform:rotate(1turn)}}@keyframes webx-banner-float{to{transform:translateY(-6%)}}@media(prefers-reduced-motion:reduce){.webx-banner__layer,.webx-banner__layer>.webx-banner__inner{animation:none!important}}.wx-banner-layers[data-v-540c304c]{display:flex;flex-direction:column;gap:var(--wx-space-12);container-type:inline-size;min-width:0}.wx-banner-layers__bar[data-v-540c304c]{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--wx-space-8)}.wx-banner-layers__tools[data-v-540c304c]{display:flex;flex-wrap:wrap;gap:var(--wx-space-4)}.wx-banner-layers__body[data-v-540c304c]{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--wx-space-16)}@container (min-width: 900px){.wx-banner-layers__body[data-v-540c304c]{grid-template-columns:minmax(0,1fr) 320px;align-items:start}}.wx-banner-layers__main[data-v-540c304c]{display:flex;flex-direction:column;gap:var(--wx-space-8);min-width:0}.wx-banner-layers__canvas[data-v-540c304c]{position:relative;isolation:isolate;overflow:hidden;border-radius:var(--wx-radius-sm);background:var(--wx-bg-muted, var(--wx-bg-surface));outline-offset:2px;user-select:none;touch-action:none}.wx-banner-layers__canvas.is-overflow[data-v-540c304c]{padding-bottom:24%;background-image:repeating-linear-gradient(45deg,transparent 0 8px,color-mix(in srgb,var(--wx-text-muted) 12%,transparent) 8px 9px)}.wx-banner-layers__canvas[data-v-540c304c]:focus-visible{outline:2px solid var(--wx-color-primary)}.wx-banner-layers__frame[data-v-540c304c]{overflow:visible}.wx-banner-layers__frame[data-v-540c304c] .webx-banner__stage{pointer-events:auto}.wx-banner-layers__frame[data-v-540c304c] .webx-banner__layer,.wx-banner-layers__frame[data-v-540c304c] .webx-banner__inner{animation:none!important;cursor:move;pointer-events:auto}.wx-banner-layers__words[data-v-540c304c]{white-space:pre-line}.wx-banner-layers__frame[data-v-540c304c] .webx-banner__layer.is-selected{outline:2px solid var(--wx-color-primary);outline-offset:1px}.wx-banner-layers__frame[data-v-540c304c] .webx-banner__layer:hover:not(.is-selected){outline:1px dashed var(--wx-color-primary)}.wx-banner-layers__frame[data-v-540c304c] .webx-banner__layer.is-outside{opacity:.5}.wx-banner-layers__handle[data-v-540c304c]{position:absolute;width:10px;height:10px;border:2px solid var(--wx-color-primary);border-radius:2px;background:var(--wx-bg-surface);translate:50% -50%}.wx-banner-layers__handle--width[data-v-540c304c]{top:50%;right:0;cursor:ew-resize}.wx-banner-layers__handle--scale[data-v-540c304c]{right:0;bottom:0;translate:50% 50%;cursor:nwse-resize}.wx-banner-layers__crop[data-v-540c304c]{position:absolute;z-index:1000;box-shadow:0 0 0 100vmax #00000073;outline:1px dashed rgb(255 255 255 / .8);pointer-events:none}.wx-banner-layers__guide[data-v-540c304c]{position:absolute;z-index:1001;background:var(--wx-color-primary);pointer-events:none}.wx-banner-layers__guide--x[data-v-540c304c]{top:0;bottom:0;width:1px}.wx-banner-layers__guide--y[data-v-540c304c]{left:0;right:0;height:1px}.wx-banner-layers__empty[data-v-540c304c]{display:grid;place-items:center;height:100%;color:var(--wx-text-muted)}.wx-banner-layers__row[data-v-540c304c]{display:flex;align-items:center;gap:var(--wx-space-8);width:100%;min-width:0;padding:0;border:0;background:none;color:inherit;font:inherit;text-align:start;cursor:pointer}.wx-banner-layers__row.is-selected[data-v-540c304c]{color:var(--wx-color-primary);font-weight:600}.wx-banner-layers__name[data-v-540c304c]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wx-banner-layers__warn[data-v-540c304c]{color:var(--wx-color-warning)}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webx-ui/module-banners",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Banners for the WebX UI admin panel: pictures, a video and up to three buttons in named places, arranged by hand and printed by the site's own templates.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -44,10 +44,10 @@
|
|
|
44
44
|
"vue-router": "^4.5.0"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@webx-ui/core": "^0.
|
|
48
|
-
"@webx-ui/module-admin": "^0.
|
|
49
|
-
"@webx-ui/module-media": "^0.10.
|
|
50
|
-
"@webx-ui/schema": "^0.9.
|
|
47
|
+
"@webx-ui/core": "^0.41.0",
|
|
48
|
+
"@webx-ui/module-admin": "^0.27.0",
|
|
49
|
+
"@webx-ui/module-media": "^0.10.4",
|
|
50
|
+
"@webx-ui/schema": "^0.9.1"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@types/node": "^24.10.1",
|