@webx-ui/module-banners 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,979 @@
1
+ import { defineComponent as X, computed as $, ref as W, watch as j, onMounted as ke, onBeforeUnmount as Ce, openBlock as m, createElementBlock as M, Fragment as H, createVNode as c, unref as e, withCtx as p, createSlots as _e, createTextVNode as E, toDisplayString as L, createBlock as D, createCommentVNode as A, useTemplateRef as Ne, nextTick as qe, isRef as De, createElementVNode as R, withKeys as me, normalizeClass as ae, resolveDynamicComponent as Ie, renderList as Oe } from "vue";
2
+ import { useRoute as ie, useRouter as ce, onBeforeRouteLeave as Le } from "vue-router";
3
+ import { useLocales as xe, localizedValue as Me, confirm as G, WxSkeleton as Q, WxCard as te, WxBadge as ne, WxFormItem as le, WxSelect as Re, WxActionBar as Ue, WxButton as se, toast as T, useModal as ze, WxDialog as Ae, WxInput as ye, WxSpace as Ee, WxTooltip as Ke, WxIcon as re, WxSegmented as Fe, WxEmpty as oe, WxSortableList as Je, WxAlert as je, createModal as He, WxListDetail as Ge, WxText as he, WxIndicator as Qe } from "@webx-ui/core";
4
+ import { useAdmin as J, useTranslate as Y, useErrorText as ue, WxScreenHead as $e, WxScreen as Xe, WxSaveState as Ye, WxRowMenu as Pe, WxListScreen as Ze } from "@webx-ui/module-admin";
5
+ function Z(i) {
6
+ const s = `${i.apiPath}/banners`, u = (n) => `${s}/places/${encodeURIComponent(n)}`, y = (n) => n.data;
7
+ return {
8
+ places: () => i.http.get(`${s}/places`).then(y),
9
+ createPlace: (n) => i.http.post(`${s}/places`, n).then(y),
10
+ renamePlace: (n, v) => i.http.put(u(n), { title: v }).then(y),
11
+ removePlace: (n) => i.http.delete(u(n)).then(() => {
12
+ }),
13
+ list: (n, v = !1) => i.http.get(`${u(n)}/banners${v ? "?trashed=1" : ""}`).then(y),
14
+ get: (n) => i.http.get(`${s}/${n}`).then(y),
15
+ create: (n, v) => i.http.post(`${u(n)}/banners`, { values: v }).then(y),
16
+ save: (n, v, d) => i.http.put(
17
+ `${s}/${n}`,
18
+ d === void 0 ? { values: v } : { values: v, place: d }
19
+ ).then(y),
20
+ remove: (n) => i.http.delete(`${s}/${n}`).then(() => {
21
+ }),
22
+ // The resource of the list line, and not the form's pair: a restored banner stays closed.
23
+ restore: (n) => i.http.post(`${s}/${n}/restore`, {}).then(y),
24
+ reorder: (n, v) => i.http.post(`${u(n)}/reorder`, { ids: v }).then(() => {
25
+ })
26
+ };
27
+ }
28
+ const ea = {
29
+ module: {
30
+ banners: "Banners"
31
+ },
32
+ places: {
33
+ hero: "Home page slider",
34
+ promo: "Promo strip",
35
+ list: "Places",
36
+ new: "New place",
37
+ title: "Name",
38
+ key: "Key",
39
+ "key-help": "What the site’s template asks for: lowercase Latin letters, digits and dashes, starting with a letter. It cannot be changed later.",
40
+ declared: "Declared in the configuration",
41
+ "in-template": "In a template: banners(':key')",
42
+ count: "Banners",
43
+ rename: "Rename",
44
+ delete: "Delete",
45
+ "delete-confirm": "Delete the place “:title”?",
46
+ "delete-not-empty": "Only an empty place can be deleted — the bin counts too.",
47
+ empty: "No places yet",
48
+ choose: "Choose a place on the left",
49
+ create: "Create",
50
+ save: "Save",
51
+ cancel: "Cancel",
52
+ created: "Place created",
53
+ renamed: "Place renamed",
54
+ deleted: "Place deleted",
55
+ places: "Places",
56
+ "new-title": "New place",
57
+ "rename-title": "Rename the place",
58
+ "delete-title": "Delete the place “:place”?",
59
+ "delete-text": "There is nothing in it. A template that still asks for it gets an empty list, not an error.",
60
+ "delete-empty-first": "Delete: empty it first",
61
+ "delete-blocked": "Banners in this place, the bin included: :count. Take them out first.",
62
+ "field-title": "Name",
63
+ "field-key": "Key",
64
+ "declared-help": "Cannot be renamed or deleted here: a template asks for it by its key.",
65
+ usage: "In a template:",
66
+ "layout-single": "One banner",
67
+ "layout-random": "A random one",
68
+ "layout-slider": "Slider",
69
+ "empty-help": "The site’s config declares the places its templates ask for.",
70
+ "choose-help": "Its banners open beside the list."
71
+ },
72
+ banner: {
73
+ new: "New banner",
74
+ trash: "Bin",
75
+ "to-trash": "Move to the bin",
76
+ restore: "Restore",
77
+ open: "Open",
78
+ enable: "Turn on",
79
+ disable: "Turn off",
80
+ disabled: "Off",
81
+ video: "Video",
82
+ empty: "No banners in this place yet",
83
+ "trash-empty": "The bin is empty",
84
+ place: "Place",
85
+ "place-help": "Moving the banner puts it at the end of the new place.",
86
+ back: "Back to the place",
87
+ save: "Save",
88
+ saved: "Banner saved",
89
+ trashed: "Bin",
90
+ restored: "Banner restored",
91
+ "leave-title": "Leave without saving?",
92
+ "leave-text": "The changes to this banner will be lost.",
93
+ leave: "Leave",
94
+ stay: "Stay",
95
+ reorder: "Reorder",
96
+ untitled: "Untitled banner",
97
+ all: "Banners",
98
+ "empty-help": "The site shows them wherever a template asks for this place.",
99
+ "empty-bin": "The bin is empty.",
100
+ "no-picture": "No picture",
101
+ order: "Drag to change the order on the site.",
102
+ "switched-on": "Switched on.",
103
+ "switched-off": "Switched off.",
104
+ delete: "Delete",
105
+ "delete-title": "Delete “:name”?",
106
+ "delete-text": "It goes to the bin and leaves the site. Restored, it comes back to its place.",
107
+ deleted: "Moved to the bin.",
108
+ "reorder-failed": "The new order was not saved.",
109
+ "save-failed": "Not saved.",
110
+ cancel: "Cancel"
111
+ },
112
+ screen: {
113
+ media: "Pictures and video",
114
+ image: "Picture",
115
+ "image-help": "Required. Shown on wide screens, and as the poster of the video.",
116
+ "image-mobile": "Picture for phones",
117
+ "image-mobile-help": "Optional. Narrow screens show it instead of the picture above.",
118
+ video: "Video",
119
+ "video-help": "Optional. Plays silently over the picture; the picture becomes its poster and what is seen without it.",
120
+ words: "Words",
121
+ title: "Title",
122
+ "title-help": "A banner with words is shown only in the languages they are written in; one without words is shown everywhere.",
123
+ text: "Text",
124
+ "text-help": "Plain text; line breaks are kept.",
125
+ buttons: "Buttons",
126
+ "buttons-help": "Up to three. A button without a label in a language is not shown in it.",
127
+ "buttons-add": "Add a button",
128
+ "buttons-empty": "No buttons",
129
+ label: "Label",
130
+ link: "Link",
131
+ variant: "Look",
132
+ "variant-placeholder": "The first one",
133
+ settings: "Settings",
134
+ enabled: "On",
135
+ "enabled-help": "Only a banner that is on is shown on the site. A new one starts off."
136
+ }
137
+ }, we = /* @__PURE__ */ new WeakSet();
138
+ function ee() {
139
+ try {
140
+ const { i18n: i } = J();
141
+ we.has(i) || (i.defaults("webx-banners", ea), we.add(i));
142
+ } catch {
143
+ }
144
+ }
145
+ const aa = /* @__PURE__ */ X({
146
+ name: "WxBannerEditorPage",
147
+ __name: "BannerEditorPage",
148
+ props: {
149
+ base: { default: "/banners" }
150
+ },
151
+ setup(i) {
152
+ const s = i, u = J(), y = Z(u), n = ie(), v = ce(), d = xe();
153
+ ee();
154
+ const l = Y("webx-banners"), t = ue(), P = $(() => {
155
+ const a = n.params.id;
156
+ return typeof a == "string" && a !== "" ? Number(a) : null;
157
+ }), b = W(null), g = W({}), S = W(""), h = W(null), k = W(null), O = W([]), C = W(!0), B = W(!1), w = W({}), x = $(() => u.can("banners.manage")), N = $(() => JSON.stringify(g.value)), U = $(
158
+ () => S.value !== "" && (N.value !== S.value || h.value !== k.value)
159
+ ), K = $(() => B.value ? "saving" : U.value || P.value === null ? "unsaved" : "saved"), F = $(() => {
160
+ const a = g.value.title, q = P.value === null ? l("banner.new") : b.value?.title ?? "";
161
+ return Me(a, d.active.value, q) || l("banner.untitled");
162
+ }), r = $(() => {
163
+ const a = k.value ?? h.value;
164
+ return a === null ? s.base : `${s.base}?place=${encodeURIComponent(a)}`;
165
+ }), I = $(
166
+ () => u.state.manifest?.modules.find((a) => a.id === "banners")?.title ?? l("module.banners")
167
+ ), f = $(
168
+ () => O.value.map((a) => ({ value: a.key, label: `${a.title} · ${a.key}` }))
169
+ ), o = $({
170
+ get: () => h.value,
171
+ set: (a) => {
172
+ h.value = a == null ? null : String(a);
173
+ }
174
+ });
175
+ function V() {
176
+ return {
177
+ image: null,
178
+ image_mobile: null,
179
+ video: null,
180
+ title: {},
181
+ text: {},
182
+ buttons: [],
183
+ enabled: !1
184
+ };
185
+ }
186
+ function _(a) {
187
+ b.value = a.banner, g.value = a.values, S.value = JSON.stringify(a.values), h.value = a.banner.place, k.value = a.banner.place;
188
+ }
189
+ async function pe() {
190
+ C.value = !0;
191
+ try {
192
+ const [a, q] = await Promise.all([
193
+ y.places(),
194
+ P.value === null ? Promise.resolve(null) : y.get(P.value)
195
+ ]);
196
+ if (O.value = a, q !== null)
197
+ _(q);
198
+ else {
199
+ const z = n.query.place, Se = typeof z == "string" && a.some((Ve) => Ve.key === z) ? z : null, fe = V();
200
+ b.value = null, g.value = fe, S.value = JSON.stringify(fe), h.value = Se ?? a[0]?.key ?? null, k.value = h.value;
201
+ }
202
+ } catch (a) {
203
+ T.danger(t(a)), v.push(s.base);
204
+ } finally {
205
+ C.value = !1;
206
+ }
207
+ }
208
+ async function ve() {
209
+ if (!(B.value || !x.value || h.value === null)) {
210
+ B.value = !0, w.value = {};
211
+ try {
212
+ const a = P.value === null, q = a ? await y.create(h.value, g.value) : await y.save(
213
+ P.value,
214
+ g.value,
215
+ h.value !== k.value ? h.value : void 0
216
+ );
217
+ _(q), T.success(l("banner.saved")), a && v.replace(`${s.base}/${q.banner.id}`);
218
+ } catch (a) {
219
+ const q = a.body;
220
+ q?.errors ? (w.value = q.errors, T.danger(l("banner.save-failed"))) : T.danger(t(a));
221
+ } finally {
222
+ B.value = !1;
223
+ }
224
+ }
225
+ }
226
+ async function Be() {
227
+ const a = b.value;
228
+ if (!(!a || !await G({
229
+ title: l("banner.delete-title", { name: a.title }),
230
+ message: l("banner.delete-text"),
231
+ confirmText: l("banner.delete"),
232
+ cancelText: l("banner.cancel"),
233
+ tone: "danger"
234
+ })))
235
+ try {
236
+ await y.remove(a.id), T.success(l("banner.deleted")), S.value = N.value, h.value = k.value, v.push(r.value);
237
+ } catch (z) {
238
+ T.danger(t(z));
239
+ }
240
+ }
241
+ function be(a) {
242
+ U.value && a.preventDefault();
243
+ }
244
+ function We(a) {
245
+ (a.ctrlKey || a.metaKey) && a.key.toLowerCase() === "s" && (a.preventDefault(), (U.value || P.value === null) && ve());
246
+ }
247
+ j(P, (a) => {
248
+ a !== null && b.value?.id === a || pe();
249
+ }), ke(() => {
250
+ pe(), window.addEventListener("beforeunload", be);
251
+ }), Ce(() => {
252
+ window.removeEventListener("beforeunload", be);
253
+ }), Le(async () => !U.value || !x.value ? !0 : await G({
254
+ title: l("banner.leave-title"),
255
+ message: l("banner.leave-text"),
256
+ confirmText: l("banner.leave"),
257
+ cancelText: l("banner.cancel"),
258
+ tone: "danger"
259
+ }));
260
+ const Te = $(
261
+ () => x.value && b.value !== null ? [
262
+ {
263
+ key: "delete",
264
+ label: l("banner.delete"),
265
+ icon: "trash",
266
+ danger: !0,
267
+ menu: !0,
268
+ run: () => {
269
+ Be();
270
+ }
271
+ }
272
+ ] : []
273
+ );
274
+ return (a, q) => (m(), M("div", {
275
+ class: "wx-banner-editor",
276
+ onKeydown: We
277
+ }, [
278
+ C.value ? (m(), M(H, { key: 0 }, [
279
+ c(e(Q), {
280
+ class: "wx-banner-editor__ghost",
281
+ title: "",
282
+ rows: 1
283
+ }),
284
+ c(e(te), null, {
285
+ default: p(() => [
286
+ c(e(Q), { rows: 6 })
287
+ ]),
288
+ _: 1
289
+ })
290
+ ], 64)) : (m(), M(H, { key: 1 }, [
291
+ c(e($e), {
292
+ divider: "",
293
+ back: r.value,
294
+ "back-label": I.value,
295
+ title: F.value,
296
+ actions: Te.value
297
+ }, _e({ _: 2 }, [
298
+ b.value && !b.value.enabled ? {
299
+ name: "title-after",
300
+ fn: p(() => [
301
+ c(e(ne), { dot: "" }, {
302
+ default: p(() => [
303
+ E(L(e(l)("banner.disabled")), 1)
304
+ ]),
305
+ _: 1
306
+ })
307
+ ]),
308
+ key: "0"
309
+ } : void 0
310
+ ]), 1032, ["back", "back-label", "title", "actions"]),
311
+ c(e(te), { class: "wx-banner-editor__place" }, {
312
+ default: p(() => [
313
+ c(e(le), {
314
+ label: e(l)("banner.place"),
315
+ help: P.value === null ? void 0 : e(l)("banner.place-help"),
316
+ error: w.value.place?.[0],
317
+ required: ""
318
+ }, {
319
+ default: p(() => [
320
+ c(e(Re), {
321
+ modelValue: o.value,
322
+ "onUpdate:modelValue": q[0] || (q[0] = (z) => o.value = z),
323
+ options: f.value,
324
+ disabled: !x.value || B.value,
325
+ "aria-label": e(l)("banner.place")
326
+ }, null, 8, ["modelValue", "options", "disabled", "aria-label"])
327
+ ]),
328
+ _: 1
329
+ }, 8, ["label", "help", "error"])
330
+ ]),
331
+ _: 1
332
+ }),
333
+ c(e(Xe), {
334
+ modelValue: g.value,
335
+ "onUpdate:modelValue": q[1] || (q[1] = (z) => g.value = z),
336
+ name: "banners.form",
337
+ errors: w.value,
338
+ disabled: !x.value || B.value
339
+ }, null, 8, ["modelValue", "errors", "disabled"]),
340
+ x.value ? (m(), D(e(Ue), { key: 0 }, {
341
+ state: p(() => [
342
+ c(e(Ye), { state: K.value }, null, 8, ["state"])
343
+ ]),
344
+ default: p(() => [
345
+ c(e(se), {
346
+ type: "primary",
347
+ loading: B.value,
348
+ disabled: !U.value && P.value !== null || h.value === null,
349
+ onClick: ve
350
+ }, {
351
+ default: p(() => [
352
+ E(L(e(l)("banner.save")), 1)
353
+ ]),
354
+ _: 1
355
+ }, 8, ["loading", "disabled"])
356
+ ]),
357
+ _: 1
358
+ })) : A("", !0)
359
+ ], 64))
360
+ ], 32));
361
+ }
362
+ }), de = (i, s) => {
363
+ const u = i.__vccOpts || i;
364
+ for (const [y, n] of s)
365
+ u[y] = n;
366
+ return u;
367
+ }, ge = /* @__PURE__ */ de(aa, [["__scopeId", "data-v-038085a4"]]), ta = /* @__PURE__ */ X({
368
+ name: "WxBannerPlaceDialog",
369
+ __name: "PlaceDialog",
370
+ props: {
371
+ place: { default: null }
372
+ },
373
+ setup(i) {
374
+ const s = i, { open: u, resolve: y, dismiss: n } = ze(), v = Z(J()), d = xe();
375
+ ee();
376
+ const l = Y("webx-banners"), t = s.place !== null;
377
+ function P() {
378
+ if (s.place === null) return {};
379
+ if (s.place.titles) return { ...s.place.titles };
380
+ const w = d.list.value[0]?.code;
381
+ return w === void 0 ? {} : { [w]: s.place.title };
382
+ }
383
+ const b = W(s.place?.key ?? ""), g = W(P()), S = W(!1), h = W({}), k = Ne("field");
384
+ ke(async () => {
385
+ await qe(), k.value?.querySelector("input")?.focus();
386
+ });
387
+ function O() {
388
+ return Object.values(g.value).some((w) => w.trim() !== "");
389
+ }
390
+ function C(w) {
391
+ const x = Object.keys(h.value).find((N) => N === w || N.startsWith(`${w}.`));
392
+ return x === void 0 ? void 0 : h.value[x]?.[0];
393
+ }
394
+ async function B() {
395
+ if (!(!O() || S.value)) {
396
+ S.value = !0, h.value = {};
397
+ try {
398
+ y(
399
+ t && s.place !== null ? await v.renamePlace(s.place.key, g.value) : await v.createPlace({ key: b.value.trim(), title: g.value })
400
+ );
401
+ } catch (w) {
402
+ h.value = w.body?.errors ?? {};
403
+ } finally {
404
+ S.value = !1;
405
+ }
406
+ }
407
+ }
408
+ return (w, x) => (m(), D(e(Ae), {
409
+ open: e(u),
410
+ "onUpdate:open": x[3] || (x[3] = (N) => De(u) ? u.value = N : null),
411
+ title: t ? e(l)("places.rename-title") : e(l)("places.new-title"),
412
+ width: 440
413
+ }, {
414
+ footer: p(() => [
415
+ c(e(Ee), { size: "sm" }, {
416
+ default: p(() => [
417
+ c(e(se), {
418
+ variant: "outline",
419
+ onClick: x[2] || (x[2] = (N) => e(n)())
420
+ }, {
421
+ default: p(() => [
422
+ E(L(e(l)("places.cancel")), 1)
423
+ ]),
424
+ _: 1
425
+ }),
426
+ c(e(se), {
427
+ type: "primary",
428
+ loading: S.value,
429
+ disabled: !O() || b.value.trim() === "",
430
+ onClick: B
431
+ }, {
432
+ default: p(() => [
433
+ E(L(t ? e(l)("places.save") : e(l)("places.create")), 1)
434
+ ]),
435
+ _: 1
436
+ }, 8, ["loading", "disabled"])
437
+ ]),
438
+ _: 1
439
+ })
440
+ ]),
441
+ default: p(() => [
442
+ c(e(le), {
443
+ label: e(l)("places.field-title"),
444
+ error: C("title"),
445
+ required: ""
446
+ }, {
447
+ default: p(() => [
448
+ R("div", {
449
+ ref_key: "field",
450
+ ref: k
451
+ }, [
452
+ c(e(ye), {
453
+ modelValue: g.value,
454
+ "onUpdate:modelValue": x[0] || (x[0] = (N) => g.value = N),
455
+ localized: "",
456
+ "aria-label": e(l)("places.field-title"),
457
+ onKeyup: me(B, ["enter"])
458
+ }, null, 8, ["modelValue", "aria-label"])
459
+ ], 512)
460
+ ]),
461
+ _: 1
462
+ }, 8, ["label", "error"]),
463
+ c(e(le), {
464
+ label: e(l)("places.field-key"),
465
+ error: C("key"),
466
+ help: t ? void 0 : e(l)("places.key-help"),
467
+ required: ""
468
+ }, {
469
+ default: p(() => [
470
+ c(e(ye), {
471
+ modelValue: b.value,
472
+ "onUpdate:modelValue": x[1] || (x[1] = (N) => b.value = N),
473
+ disabled: t,
474
+ "aria-label": e(l)("places.field-key"),
475
+ autocomplete: "off",
476
+ onKeyup: me(B, ["enter"])
477
+ }, null, 8, ["modelValue", "aria-label"])
478
+ ]),
479
+ _: 1
480
+ }, 8, ["label", "error", "help"])
481
+ ]),
482
+ _: 1
483
+ }, 8, ["open", "title"]));
484
+ }
485
+ }), na = { class: "wx-banner-place" }, la = ["aria-label"], sa = { class: "wx-banner-place__usage" }, ra = {
486
+ key: 0,
487
+ class: "wx-banner-place__layout"
488
+ }, oa = { class: "wx-banner-row__thumb" }, ia = ["src"], ca = { class: "wx-banner-row__body" }, ua = { class: "wx-banner-row__title" }, da = {
489
+ key: 0,
490
+ class: "wx-banner-row__facts"
491
+ }, pa = /* @__PURE__ */ X({
492
+ name: "WxBannerPlacePane",
493
+ __name: "PlacePane",
494
+ props: {
495
+ place: {},
496
+ base: {},
497
+ inline: { type: Boolean, default: !0 }
498
+ },
499
+ emits: ["back", "changed"],
500
+ setup(i, { emit: s }) {
501
+ const u = i, y = s, n = J(), v = Z(n), d = ie(), l = ce();
502
+ ee();
503
+ const t = Y("webx-banners"), P = ue(), b = W([]), g = W(!0), S = $(() => n.can("banners.manage")), h = $({
504
+ get: () => d.query.view === "trashed" ? "trashed" : "live",
505
+ set: (o) => {
506
+ l.replace({
507
+ query: { ...d.query, view: o === "trashed" ? "trashed" : void 0 }
508
+ });
509
+ }
510
+ }), k = $(() => h.value === "trashed"), O = $(() => [
511
+ { value: "live", label: t("banner.all") },
512
+ { value: "trashed", label: t("banner.trashed"), icon: "trash" }
513
+ ]), C = $(() => S.value && !k.value && b.value.length > 1), B = $(() => ["single", "random", "slider"].includes(u.place.layout) ? t(`places.layout-${u.place.layout}`) : "");
514
+ async function w() {
515
+ g.value = !0;
516
+ try {
517
+ b.value = await v.list(u.place.key, k.value);
518
+ } catch (o) {
519
+ T.danger(P(o));
520
+ } finally {
521
+ g.value = !1;
522
+ }
523
+ }
524
+ j(k, () => {
525
+ w();
526
+ }), w();
527
+ function x(o) {
528
+ return `${u.base}/${o.id}`;
529
+ }
530
+ function N() {
531
+ l.push({ path: `${u.base}/new`, query: { place: u.place.key } });
532
+ }
533
+ async function U() {
534
+ try {
535
+ await v.reorder(
536
+ u.place.key,
537
+ b.value.map((o) => o.id)
538
+ );
539
+ } catch (o) {
540
+ T.danger(P(o, t("banner.reorder-failed"))), await w();
541
+ }
542
+ }
543
+ async function K(o) {
544
+ try {
545
+ const V = await v.get(o.id);
546
+ await v.save(o.id, { ...V.values, enabled: !o.enabled }), T.success(o.enabled ? t("banner.switched-off") : t("banner.switched-on"));
547
+ } catch (V) {
548
+ T.danger(P(V));
549
+ } finally {
550
+ await w();
551
+ }
552
+ }
553
+ async function F(o) {
554
+ if (await G({
555
+ title: t("banner.delete-title", { name: o.title }),
556
+ message: t("banner.delete-text"),
557
+ confirmText: t("banner.delete"),
558
+ cancelText: t("banner.cancel"),
559
+ tone: "danger"
560
+ }))
561
+ try {
562
+ await v.remove(o.id), T.success(t("banner.deleted")), y("changed");
563
+ } catch (_) {
564
+ T.danger(P(_));
565
+ } finally {
566
+ await w();
567
+ }
568
+ }
569
+ async function r(o) {
570
+ try {
571
+ await v.restore(o.id), T.success(t("banner.restored")), y("changed");
572
+ } catch (V) {
573
+ T.danger(P(V));
574
+ } finally {
575
+ await w();
576
+ }
577
+ }
578
+ function I(o) {
579
+ if (k.value)
580
+ return S.value ? [
581
+ {
582
+ key: "restore",
583
+ icon: "refresh",
584
+ label: t("banner.restore"),
585
+ run: () => {
586
+ r(o);
587
+ }
588
+ }
589
+ ] : [];
590
+ const V = {
591
+ key: "open",
592
+ icon: "edit",
593
+ label: t("banner.open"),
594
+ run: () => {
595
+ l.push(x(o));
596
+ }
597
+ };
598
+ return S.value ? [
599
+ V,
600
+ {
601
+ key: "toggle",
602
+ icon: o.enabled ? "eye-off" : "eye",
603
+ label: o.enabled ? t("banner.disable") : t("banner.enable"),
604
+ run: () => {
605
+ K(o);
606
+ }
607
+ },
608
+ {
609
+ key: "delete",
610
+ icon: "trash",
611
+ label: t("banner.delete"),
612
+ danger: !0,
613
+ run: () => {
614
+ F(o);
615
+ }
616
+ }
617
+ ] : [V];
618
+ }
619
+ const f = $(
620
+ () => S.value && !k.value ? [{ key: "new", label: t("banner.new"), icon: "plus", primary: !0, run: N }] : []
621
+ );
622
+ return (o, V) => (m(), M("div", na, [
623
+ c(e($e), {
624
+ level: 3,
625
+ back: !u.inline,
626
+ "back-label": e(t)("places.places"),
627
+ title: i.place.title,
628
+ actions: f.value,
629
+ "collapse-below": 420,
630
+ onBack: V[0] || (V[0] = (_) => y("back"))
631
+ }, _e({ _: 2 }, [
632
+ i.place.declared ? {
633
+ name: "title-after",
634
+ fn: p(() => [
635
+ c(e(Ke), {
636
+ content: e(t)("places.declared-help")
637
+ }, {
638
+ default: p(() => [
639
+ R("span", {
640
+ class: "wx-banner-place__lock",
641
+ tabindex: "0",
642
+ "aria-label": e(t)("places.declared")
643
+ }, [
644
+ c(e(re), {
645
+ name: "lock",
646
+ size: "sm"
647
+ })
648
+ ], 8, la)
649
+ ]),
650
+ _: 1
651
+ }, 8, ["content"])
652
+ ]),
653
+ key: "0"
654
+ } : void 0
655
+ ]), 1032, ["back", "back-label", "title", "actions"]),
656
+ R("p", sa, [
657
+ R("span", null, L(e(t)("places.usage")), 1),
658
+ R("code", null, "banners('" + L(i.place.key) + "')", 1),
659
+ B.value ? (m(), M("span", ra, "· " + L(B.value), 1)) : A("", !0)
660
+ ]),
661
+ c(e(Fe), {
662
+ modelValue: h.value,
663
+ "onUpdate:modelValue": V[1] || (V[1] = (_) => h.value = _),
664
+ class: "wx-banner-place__views",
665
+ size: "sm",
666
+ options: O.value,
667
+ "aria-label": e(t)("banner.all")
668
+ }, null, 8, ["modelValue", "options", "aria-label"]),
669
+ g.value && b.value.length === 0 ? (m(), D(e(Q), {
670
+ key: 0,
671
+ rows: 4
672
+ })) : b.value.length === 0 ? (m(), D(e(oe), {
673
+ key: 1,
674
+ title: k.value ? e(t)("banner.empty-bin") : e(t)("banner.empty"),
675
+ description: k.value ? void 0 : e(t)("banner.empty-help")
676
+ }, null, 8, ["title", "description"])) : (m(), M(H, { key: 2 }, [
677
+ c(e(Je), {
678
+ modelValue: b.value,
679
+ "onUpdate:modelValue": V[2] || (V[2] = (_) => b.value = _),
680
+ class: ae(["wx-banner-place__list", { "is-loading": g.value }]),
681
+ plain: "",
682
+ "item-key": "id",
683
+ handle: C.value ? "grip" : "row",
684
+ "item-label": (_) => _.title,
685
+ disabled: !C.value,
686
+ onMove: U
687
+ }, {
688
+ default: p(({ item: _ }) => [
689
+ (m(), D(Ie(k.value ? "div" : "router-link"), {
690
+ to: k.value ? void 0 : x(_),
691
+ class: ae(["wx-banner-row", { "is-off": !_.enabled && !k.value }])
692
+ }, {
693
+ default: p(() => [
694
+ R("span", oa, [
695
+ _.thumb ? (m(), M("img", {
696
+ key: 0,
697
+ src: _.thumb,
698
+ alt: "",
699
+ loading: "lazy"
700
+ }, null, 8, ia)) : (m(), D(e(re), {
701
+ key: 1,
702
+ name: "image",
703
+ size: "sm",
704
+ "aria-label": e(t)("banner.no-picture")
705
+ }, null, 8, ["aria-label"]))
706
+ ]),
707
+ R("span", ca, [
708
+ R("span", ua, L(_.title), 1),
709
+ _.video || !_.enabled && !k.value ? (m(), M("span", da, [
710
+ _.video ? (m(), D(e(ne), {
711
+ key: 0,
712
+ size: "sm",
713
+ type: "info"
714
+ }, {
715
+ default: p(() => [
716
+ E(L(e(t)("banner.video")), 1)
717
+ ]),
718
+ _: 1
719
+ })) : A("", !0),
720
+ !_.enabled && !k.value ? (m(), D(e(ne), {
721
+ key: 1,
722
+ dot: "",
723
+ size: "sm"
724
+ }, {
725
+ default: p(() => [
726
+ E(L(e(t)("banner.disabled")), 1)
727
+ ]),
728
+ _: 1
729
+ })) : A("", !0)
730
+ ])) : A("", !0)
731
+ ])
732
+ ]),
733
+ _: 2
734
+ }, 1032, ["to", "class"]))
735
+ ]),
736
+ actions: p(({ item: _ }) => [
737
+ c(e(Pe), {
738
+ actions: I(_),
739
+ label: _.title
740
+ }, null, 8, ["actions", "label"])
741
+ ]),
742
+ _: 1
743
+ }, 8, ["modelValue", "class", "handle", "item-label", "disabled"]),
744
+ C.value ? (m(), D(e(je), {
745
+ key: 0,
746
+ class: "wx-banner-place__note",
747
+ type: "info",
748
+ description: e(t)("banner.order")
749
+ }, null, 8, ["description"])) : A("", !0)
750
+ ], 64))
751
+ ]));
752
+ }
753
+ }), va = /* @__PURE__ */ de(pa, [["__scopeId", "data-v-23c95ae5"]]), ba = {
754
+ key: 2,
755
+ class: "wx-banners__list"
756
+ }, fa = ["onClick"], ma = { class: "wx-banners__name" }, ya = /* @__PURE__ */ X({
757
+ __name: "BannersPage",
758
+ props: {
759
+ base: { default: "/banners" }
760
+ },
761
+ setup(i) {
762
+ const s = i, u = J(), y = Z(u), n = ie(), v = ce();
763
+ ee();
764
+ const d = Y("webx-banners"), l = ue(), t = W([]), P = W(!0), b = W(!1), g = W(!0), S = $(() => u.can("banners.manage")), h = He(ta), k = $(
765
+ () => u.state.manifest?.modules.find((r) => r.id === "banners")?.title ?? d("module.banners")
766
+ ), O = $(() => {
767
+ const r = n.query.place;
768
+ return typeof r == "string" && r !== "" ? r : null;
769
+ }), C = $(() => t.value.find((r) => r.key === O.value) ?? null);
770
+ async function B() {
771
+ P.value = !0;
772
+ try {
773
+ t.value = await y.places();
774
+ } catch (r) {
775
+ T.danger(l(r));
776
+ } finally {
777
+ P.value = !1;
778
+ }
779
+ }
780
+ B(), j([t, C, g], () => {
781
+ !g.value || t.value.length === 0 || C.value !== null || v.replace({ query: { ...n.query, place: t.value[0].key } });
782
+ }), j(C, (r) => {
783
+ r !== null && g.value && (b.value = !0);
784
+ }), O.value !== null && (b.value = !0);
785
+ function w(r) {
786
+ const I = { ...n.query, place: r.key };
787
+ delete I.view, v.replace({ query: I }), b.value = !0;
788
+ }
789
+ async function x() {
790
+ const r = await h({ place: null });
791
+ r && (T.success(d("places.created")), await B(), w(r));
792
+ }
793
+ async function N(r) {
794
+ await h({ place: r }) && (T.success(d("places.renamed")), await B());
795
+ }
796
+ async function U(r) {
797
+ if (await G({
798
+ title: d("places.delete-title", { place: r.title }),
799
+ message: d("places.delete-text"),
800
+ confirmText: d("places.delete"),
801
+ cancelText: d("places.cancel"),
802
+ tone: "danger"
803
+ }))
804
+ try {
805
+ if (await y.removePlace(r.key), T.success(d("places.deleted")), O.value === r.key) {
806
+ const f = { ...n.query };
807
+ delete f.place, delete f.view, v.replace({ query: f }), b.value = !1;
808
+ }
809
+ } catch (f) {
810
+ T.danger(l(f));
811
+ } finally {
812
+ await B();
813
+ }
814
+ }
815
+ function K(r) {
816
+ return !S.value || r.declared ? [] : [
817
+ { key: "rename", icon: "edit", label: d("places.rename"), run: () => {
818
+ N(r);
819
+ } },
820
+ {
821
+ key: "delete",
822
+ icon: "trash",
823
+ // Said in the line itself, since a switched-off line cannot carry a tooltip: an empty
824
+ // place is the only kind that goes (decision 1).
825
+ label: r.count > 0 ? d("places.delete-empty-first") : d("places.delete"),
826
+ danger: !0,
827
+ disabled: r.count > 0,
828
+ run: () => {
829
+ U(r);
830
+ }
831
+ }
832
+ ];
833
+ }
834
+ const F = $(
835
+ () => S.value ? [{ key: "new-place", label: d("places.new"), icon: "plus", run: () => {
836
+ x();
837
+ } }] : []
838
+ );
839
+ return (r, I) => (m(), D(e(Ze), {
840
+ title: k.value,
841
+ actions: F.value,
842
+ card: !1
843
+ }, {
844
+ default: p(() => [
845
+ c(e(te), {
846
+ class: "wx-banners",
847
+ padding: "none"
848
+ }, {
849
+ default: p(() => [
850
+ c(e(Ge), {
851
+ open: b.value,
852
+ "onUpdate:open": I[0] || (I[0] = (f) => b.value = f),
853
+ class: "wx-banners__panes",
854
+ "list-width": 300,
855
+ "detail-min": 440,
856
+ "detail-label": e(d)("places.places"),
857
+ onDetailInline: I[1] || (I[1] = (f) => g.value = f)
858
+ }, {
859
+ list: p(() => [
860
+ P.value && t.value.length === 0 ? (m(), D(e(Q), {
861
+ key: 0,
862
+ class: "wx-banners__loading",
863
+ rows: 3
864
+ })) : t.value.length === 0 ? (m(), D(e(oe), {
865
+ key: 1,
866
+ title: e(d)("places.empty"),
867
+ description: e(d)("places.empty-help")
868
+ }, null, 8, ["title", "description"])) : (m(), M("ul", ba, [
869
+ (m(!0), M(H, null, Oe(t.value, (f) => (m(), M("li", {
870
+ key: f.key,
871
+ class: "wx-banners__row"
872
+ }, [
873
+ R("button", {
874
+ type: "button",
875
+ class: ae(["wx-banners__open", { "is-current": f.key === O.value }]),
876
+ onClick: (o) => w(f)
877
+ }, [
878
+ R("span", ma, [
879
+ c(e(he), {
880
+ truncate: "",
881
+ weight: "medium"
882
+ }, {
883
+ default: p(() => [
884
+ E(L(f.title), 1)
885
+ ]),
886
+ _: 2
887
+ }, 1024),
888
+ f.declared ? (m(), D(e(re), {
889
+ key: 0,
890
+ class: "wx-banners__lock",
891
+ name: "lock",
892
+ size: "sm",
893
+ "aria-label": e(d)("places.declared")
894
+ }, null, 8, ["aria-label"])) : A("", !0),
895
+ c(e(Qe), {
896
+ class: "wx-banners__count",
897
+ type: "neutral",
898
+ value: f.count,
899
+ label: e(d)("places.count")
900
+ }, null, 8, ["value", "label"])
901
+ ]),
902
+ c(e(he), {
903
+ size: "sm",
904
+ tone: "muted",
905
+ mono: "",
906
+ truncate: ""
907
+ }, {
908
+ default: p(() => [
909
+ E("banners('" + L(f.key) + "')", 1)
910
+ ]),
911
+ _: 2
912
+ }, 1024)
913
+ ], 10, fa),
914
+ K(f).length > 0 ? (m(), D(e(Pe), {
915
+ key: 0,
916
+ actions: K(f),
917
+ label: f.title
918
+ }, null, 8, ["actions", "label"])) : A("", !0)
919
+ ]))), 128))
920
+ ]))
921
+ ]),
922
+ detail: p(({ inline: f, back: o }) => [
923
+ C.value ? (m(), D(va, {
924
+ key: C.value.key,
925
+ place: C.value,
926
+ base: s.base,
927
+ inline: f,
928
+ onBack: o,
929
+ onChanged: B
930
+ }, null, 8, ["place", "base", "inline", "onBack"])) : A("", !0)
931
+ ]),
932
+ empty: p(() => [
933
+ c(e(oe), {
934
+ title: e(d)("places.choose"),
935
+ description: e(d)("places.choose-help")
936
+ }, null, 8, ["title", "description"])
937
+ ]),
938
+ _: 1
939
+ }, 8, ["open", "detail-label"])
940
+ ]),
941
+ _: 1
942
+ })
943
+ ]),
944
+ _: 1
945
+ }, 8, ["title", "actions"]));
946
+ }
947
+ }), ha = /* @__PURE__ */ de(ya, [["__scopeId", "data-v-615232f0"]]);
948
+ function xa(i = {}) {
949
+ const s = i.path ?? "/banners";
950
+ return {
951
+ id: "banners",
952
+ path: s,
953
+ routes: [
954
+ // The open place is in the address (`?place=`), so a link to it is a link to its banners.
955
+ { path: s, name: "webx.banners", component: ha, props: { base: s } },
956
+ // A banner not written yet: the place it goes to rides in the query (`?place=hero`).
957
+ {
958
+ path: `${s}/new`,
959
+ name: "webx.banners.new",
960
+ component: ge,
961
+ props: { base: s }
962
+ },
963
+ {
964
+ path: `${s}/:id(\\d+)`,
965
+ name: "webx.banners.edit",
966
+ component: ge,
967
+ props: { base: s }
968
+ }
969
+ ]
970
+ };
971
+ }
972
+ export {
973
+ ge as WxBannerEditorPage,
974
+ ha as WxBannersPage,
975
+ xa as banners,
976
+ ea as bannersMessages,
977
+ Z as createBannersApi
978
+ };
979
+ //# sourceMappingURL=index.js.map