@webx-ui/module-press 0.1.0 → 0.1.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  import { defineComponent as se, ref as W, computed as h, onMounted as he, onBeforeUnmount as ne, openBlock as v, createElementBlock as D, createBlock as b, unref as t, Fragment as re, createVNode as w, createSlots as fe, withCtx as p, createTextVNode as z, toDisplayString as $, createCommentVNode as O, watch as te, createElementVNode as N, normalizeClass as H, resolveDynamicComponent as me } from "vue";
2
2
  import { onBeforeRouteUpdate as we, onBeforeRouteLeave as ye, useRoute as ge, useRouter as be } from "vue-router";
3
3
  import { useLocales as _e, localizedValue as xe, WxSkeleton as ie, WxBadge as Q, WxActionBar as ke, WxButton as Se, toast as _, confirm as X, WxCard as We, WxListDetail as Te, WxEmpty as le, WxInput as Pe, WxIcon as Y, WxSortableList as Ae, WxAvatar as qe, WxTooltip as ae, WxAlert as Be } from "@webx-ui/core";
4
- import { reorderItems as $e, useAdmin as ee, useTranslate as ue, useErrorText as ce, provideRecordAddress as Le, WxScreenHead as Ne, WxScreen as Oe, WxSaveState as Ce, useItemOrder as Ve, WxListScreen as De, WxRowMenu as Ie } from "@webx-ui/module-admin";
4
+ import { reorderItems as $e, useAdmin as ee, useTranslate as ue, useErrorText as ce, provideRecordAddress as Le, useBodyKeys as Ne, WxScreenHead as Oe, WxScreen as Ce, WxSaveState as Ve, useItemOrder as De, WxListScreen as Ie, WxRowMenu as Re } from "@webx-ui/module-admin";
5
5
  const Z = "press";
6
6
  function de(u) {
7
7
  const d = `${u.apiPath}/${Z}`, i = (s) => s.data;
@@ -20,7 +20,7 @@ function de(u) {
20
20
  reorder: (s) => $e(u, Z, s, null)
21
21
  };
22
22
  }
23
- const Re = {
23
+ const ze = {
24
24
  module: {
25
25
  press: "Press"
26
26
  },
@@ -114,11 +114,11 @@ const Re = {
114
114
  function ve() {
115
115
  try {
116
116
  const { i18n: u } = ee();
117
- oe.has(u) || (u.defaults("webx-press", Re), oe.add(u));
117
+ oe.has(u) || (u.defaults("webx-press", ze), oe.add(u));
118
118
  } catch {
119
119
  }
120
120
  }
121
- const ze = /* @__PURE__ */ se({
121
+ const Ee = /* @__PURE__ */ se({
122
122
  name: "WxOutletPane",
123
123
  __name: "OutletPane",
124
124
  props: {
@@ -129,7 +129,7 @@ const ze = /* @__PURE__ */ se({
129
129
  setup(u, { emit: d }) {
130
130
  const i = u, s = d, f = ee(), a = de(f), A = _e();
131
131
  ve();
132
- const n = ue("webx-press"), q = ce(), y = W(null), x = W({}), L = W(null), m = W(""), k = W(i.id !== null), S = W(!1), g = W({}), B = h(() => f.can("press.manage")), E = h(() => JSON.stringify(x.value)), T = h(() => m.value !== "" && E.value !== m.value), U = h(() => S.value ? "saving" : T.value || i.id === null ? "unsaved" : "saved"), j = h(() => {
132
+ const n = ue("webx-press"), q = ce(), y = W(null), x = W({}), L = W(null), m = W(""), k = W(i.id !== null), S = W(!1), g = W({}), B = h(() => f.can("press.manage")), E = h(() => JSON.stringify(x.value)), T = h(() => m.value !== "" && E.value !== m.value), U = h(() => S.value ? "saving" : T.value || i.id === null ? "unsaved" : "saved"), J = h(() => {
133
133
  const l = x.value.title, r = i.id === null ? n("outlet.new") : y.value?.title ?? "";
134
134
  return xe(l, A.active.value, r) || n("outlet.untitled");
135
135
  });
@@ -190,7 +190,7 @@ const ze = /* @__PURE__ */ se({
190
190
  }
191
191
  }
192
192
  }
193
- async function J() {
193
+ async function G() {
194
194
  const l = y.value;
195
195
  if (!(!l || !await X({
196
196
  title: n("outlet.delete-title", { name: l.title }),
@@ -208,15 +208,15 @@ const ze = /* @__PURE__ */ se({
208
208
  function M(l) {
209
209
  T.value && l.preventDefault();
210
210
  }
211
- function G(l) {
211
+ function K(l) {
212
212
  (l.ctrlKey || l.metaKey) && l.key.toLowerCase() === "s" && (l.preventDefault(), (T.value || i.id === null) && R());
213
213
  }
214
- he(() => {
214
+ Ne(K), he(() => {
215
215
  I(), window.addEventListener("beforeunload", M);
216
216
  }), ne(() => {
217
217
  window.removeEventListener("beforeunload", M);
218
218
  });
219
- async function K() {
219
+ async function F() {
220
220
  return !T.value || !B.value ? !0 : await X({
221
221
  title: n("outlet.leave-title"),
222
222
  message: n("outlet.leave-text"),
@@ -226,9 +226,9 @@ const ze = /* @__PURE__ */ se({
226
226
  });
227
227
  }
228
228
  we(
229
- async (l, r) => l.query.outlet === r.query.outlet ? !0 : await K()
230
- ), ye(K);
231
- const F = h(() => {
229
+ async (l, r) => l.query.outlet === r.query.outlet ? !0 : await F()
230
+ ), ye(F);
231
+ const j = h(() => {
232
232
  const l = y.value, r = [];
233
233
  if (l?.url) {
234
234
  const e = l.url;
@@ -248,13 +248,13 @@ const ze = /* @__PURE__ */ se({
248
248
  danger: !0,
249
249
  menu: !0,
250
250
  run: () => {
251
- J();
251
+ G();
252
252
  }
253
253
  }), r;
254
254
  });
255
255
  return (l, r) => (v(), D("div", {
256
256
  class: "wx-outlet",
257
- onKeydown: G
257
+ onKeydown: K
258
258
  }, [
259
259
  k.value ? (v(), b(t(ie), {
260
260
  key: 0,
@@ -262,12 +262,12 @@ const ze = /* @__PURE__ */ se({
262
262
  title: "",
263
263
  rows: 6
264
264
  })) : (v(), D(re, { key: 1 }, [
265
- w(t(Ne), {
265
+ w(t(Oe), {
266
266
  level: 3,
267
267
  back: !i.inline,
268
268
  "back-label": t(n)("outlet.back"),
269
- title: j.value,
270
- actions: F.value,
269
+ title: J.value,
270
+ actions: j.value,
271
271
  "collapse-below": 480,
272
272
  onBack: r[0] || (r[0] = (e) => s("back"))
273
273
  }, fe({ _: 2 }, [
@@ -284,7 +284,7 @@ const ze = /* @__PURE__ */ se({
284
284
  key: "0"
285
285
  } : void 0
286
286
  ]), 1032, ["back", "back-label", "title", "actions"]),
287
- w(t(Oe), {
287
+ w(t(Ce), {
288
288
  modelValue: x.value,
289
289
  "onUpdate:modelValue": r[1] || (r[1] = (e) => x.value = e),
290
290
  name: "press.outlet-form",
@@ -293,7 +293,7 @@ const ze = /* @__PURE__ */ se({
293
293
  }, null, 8, ["modelValue", "errors", "disabled"]),
294
294
  B.value ? (v(), b(t(ke), { key: 0 }, {
295
295
  state: p(() => [
296
- w(t(Ce), { state: U.value }, null, 8, ["state"])
296
+ w(t(Ve), { state: U.value }, null, 8, ["state"])
297
297
  ]),
298
298
  default: p(() => [
299
299
  w(t(Se), {
@@ -318,10 +318,10 @@ const ze = /* @__PURE__ */ se({
318
318
  for (const [s, f] of d)
319
319
  i[s] = f;
320
320
  return i;
321
- }, Ee = /* @__PURE__ */ pe(ze, [["__scopeId", "data-v-396f08f3"]]), Ue = { class: "wx-press__bar" }, Me = {
321
+ }, Ue = /* @__PURE__ */ pe(Ee, [["__scopeId", "data-v-b6ea9c8f"]]), Me = { class: "wx-press__bar" }, Ke = {
322
322
  key: 0,
323
323
  class: "wx-outlet-row__logo"
324
- }, Ke = ["src"], Fe = { class: "wx-outlet-row__body" }, je = { class: "wx-outlet-row__head" }, Je = { class: "wx-outlet-row__title" }, Ge = { class: "wx-outlet-row__facts" }, He = { class: "wx-outlet-row__count" }, Ye = ["aria-label"], Qe = /* @__PURE__ */ se({
324
+ }, Fe = ["src"], je = { class: "wx-outlet-row__body" }, Je = { class: "wx-outlet-row__head" }, Ge = { class: "wx-outlet-row__title" }, He = { class: "wx-outlet-row__facts" }, Ye = { class: "wx-outlet-row__count" }, Qe = ["aria-label"], Xe = /* @__PURE__ */ se({
325
325
  __name: "PressPage",
326
326
  props: {
327
327
  base: { default: "/press" }
@@ -351,7 +351,7 @@ const ze = /* @__PURE__ */ se({
351
351
  }), E = h(() => [
352
352
  { value: "", label: a("outlet.all") },
353
353
  { value: "trashed", label: a("outlet.trashed"), icon: "trash" }
354
- ]), T = Ve(Z, () => ({ q: k.value, filtered: m.value })), U = h(() => y.value && T.sortable.value), j = h(() => k.value !== "" ? a("outlet.empty-search") : m.value ? a("outlet.empty-bin") : a("outlet.empty"));
354
+ ]), T = De(Z, () => ({ q: k.value, filtered: m.value })), U = h(() => y.value && T.sortable.value), J = h(() => k.value !== "" ? a("outlet.empty-search") : m.value ? a("outlet.empty-bin") : a("outlet.empty"));
355
355
  async function P() {
356
356
  q.value = !0;
357
357
  try {
@@ -382,7 +382,7 @@ const ze = /* @__PURE__ */ se({
382
382
  const e = { ...s.query };
383
383
  delete e.outlet, f.replace({ query: e });
384
384
  }
385
- function J(e) {
385
+ function G(e) {
386
386
  I(e.id), P();
387
387
  }
388
388
  async function M() {
@@ -392,7 +392,7 @@ const ze = /* @__PURE__ */ se({
392
392
  _.danger(A(e, a("outlet.reorder-failed"))), await P();
393
393
  }
394
394
  }
395
- function G(e) {
395
+ function K(e) {
396
396
  return y.value ? m.value ? [
397
397
  {
398
398
  key: "restore",
@@ -409,12 +409,12 @@ const ze = /* @__PURE__ */ se({
409
409
  label: a("outlet.delete"),
410
410
  danger: !0,
411
411
  run: () => {
412
- K(e);
412
+ F(e);
413
413
  }
414
414
  }
415
415
  ] : [];
416
416
  }
417
- async function K(e) {
417
+ async function F(e) {
418
418
  if (await X({
419
419
  title: a("outlet.delete-title", { name: e.title }),
420
420
  message: a("outlet.delete-text"),
@@ -423,12 +423,12 @@ const ze = /* @__PURE__ */ se({
423
423
  tone: "danger"
424
424
  }))
425
425
  try {
426
- await i.remove(e.id), _.success(a("outlet.deleted")), F(e.id);
426
+ await i.remove(e.id), _.success(a("outlet.deleted")), j(e.id);
427
427
  } catch (o) {
428
428
  _.danger(A(o));
429
429
  }
430
430
  }
431
- function F(e) {
431
+ function j(e) {
432
432
  g.value === e && R(), P();
433
433
  }
434
434
  async function l(e) {
@@ -443,7 +443,7 @@ const ze = /* @__PURE__ */ se({
443
443
  function r(e) {
444
444
  return e.published && e.locales.length === 0;
445
445
  }
446
- return (e, c) => (v(), b(t(De), {
446
+ return (e, c) => (v(), b(t(Ie), {
447
447
  view: L.value,
448
448
  "onUpdate:view": c[4] || (c[4] = (o) => L.value = o),
449
449
  title: x.value,
@@ -465,7 +465,7 @@ const ze = /* @__PURE__ */ se({
465
465
  "detail-label": t(a)("module.press")
466
466
  }, {
467
467
  list: p(() => [
468
- N("div", Ue, [
468
+ N("div", Me, [
469
469
  w(t(Pe), {
470
470
  modelValue: S.value,
471
471
  "onUpdate:modelValue": c[0] || (c[0] = (o) => S.value = o),
@@ -502,7 +502,7 @@ const ze = /* @__PURE__ */ se({
502
502
  rows: 5
503
503
  })) : n.value.length === 0 ? (v(), b(t(le), {
504
504
  key: 2,
505
- title: j.value,
505
+ title: J.value,
506
506
  description: !m.value && k.value === "" ? t(a)("outlet.empty-help") : void 0
507
507
  }, null, 8, ["title", "description"])) : (v(), D(re, { key: 3 }, [
508
508
  w(t(Ae), {
@@ -523,11 +523,11 @@ const ze = /* @__PURE__ */ se({
523
523
  onClick: (V) => m.value ? void 0 : I(o.id)
524
524
  }, {
525
525
  default: p(() => [
526
- o.logo?.thumb ? (v(), D("span", Me, [
526
+ o.logo?.thumb ? (v(), D("span", Ke, [
527
527
  N("img", {
528
528
  src: o.logo.thumb,
529
529
  alt: ""
530
- }, null, 8, Ke)
530
+ }, null, 8, Fe)
531
531
  ])) : (v(), b(t(qe), {
532
532
  key: 1,
533
533
  class: "wx-outlet-row__initials",
@@ -535,9 +535,9 @@ const ze = /* @__PURE__ */ se({
535
535
  name: o.title,
536
536
  size: "md"
537
537
  }, null, 8, ["name"])),
538
- N("span", Fe, [
539
- N("span", je, [
540
- N("span", Je, $(o.title), 1),
538
+ N("span", je, [
539
+ N("span", Je, [
540
+ N("span", Ge, $(o.title), 1),
541
541
  o.featured ? (v(), b(t(ae), {
542
542
  key: 0,
543
543
  content: t(a)("outlet.featured")
@@ -553,7 +553,7 @@ const ze = /* @__PURE__ */ se({
553
553
  _: 1
554
554
  }, 8, ["content"])) : O("", !0)
555
555
  ]),
556
- N("span", Ge, [
556
+ N("span", He, [
557
557
  o.published ? r(o) ? (v(), b(t(ae), {
558
558
  key: 1,
559
559
  content: t(a)("outlet.visible-nowhere")
@@ -581,12 +581,12 @@ const ze = /* @__PURE__ */ se({
581
581
  ]),
582
582
  _: 1
583
583
  })),
584
- N("span", He, $(o.articles_count > 0 ? t(a)("outlet.articles-count", { count: o.articles_count }) : t(a)("outlet.no-articles")), 1),
584
+ N("span", Ye, $(o.articles_count > 0 ? t(a)("outlet.articles-count", { count: o.articles_count }) : t(a)("outlet.no-articles")), 1),
585
585
  o.locales.length > 0 ? (v(), D("span", {
586
586
  key: 2,
587
587
  class: "wx-outlet-row__locales",
588
588
  "aria-label": t(a)("outlet.seen-in", { locales: o.locales.join(", ") })
589
- }, $(o.locales.join(" · ")), 9, Ye)) : O("", !0)
589
+ }, $(o.locales.join(" · ")), 9, Qe)) : O("", !0)
590
590
  ])
591
591
  ])
592
592
  ]),
@@ -594,8 +594,8 @@ const ze = /* @__PURE__ */ se({
594
594
  }, 1032, ["type", "class", "onClick"]))
595
595
  ]),
596
596
  actions: p(({ item: o }) => [
597
- w(t(Ie), {
598
- actions: G(o),
597
+ w(t(Re), {
598
+ actions: K(o),
599
599
  label: o.title
600
600
  }, null, 8, ["actions", "label"])
601
601
  ]),
@@ -610,14 +610,14 @@ const ze = /* @__PURE__ */ se({
610
610
  ], 64))
611
611
  ]),
612
612
  detail: p(({ inline: o, back: V }) => [
613
- g.value !== null ? (v(), b(Ee, {
613
+ g.value !== null ? (v(), b(Ue, {
614
614
  id: g.value === "new" ? null : g.value,
615
615
  key: String(g.value),
616
616
  inline: o,
617
617
  onBack: V,
618
618
  onSaved: P,
619
- onCreated: J,
620
- onRemoved: F
619
+ onCreated: G,
620
+ onRemoved: j
621
621
  }, null, 8, ["id", "inline", "onBack"])) : O("", !0)
622
622
  ]),
623
623
  empty: p(() => [
@@ -635,23 +635,23 @@ const ze = /* @__PURE__ */ se({
635
635
  _: 1
636
636
  }, 8, ["view", "title", "views"]));
637
637
  }
638
- }), Xe = /* @__PURE__ */ pe(Qe, [["__scopeId", "data-v-7ef16f83"]]);
639
- function at(u = {}) {
638
+ }), Ze = /* @__PURE__ */ pe(Xe, [["__scopeId", "data-v-7ef16f83"]]);
639
+ function ot(u = {}) {
640
640
  const d = u.path ?? "/press";
641
641
  return {
642
642
  id: "press",
643
643
  path: d,
644
644
  // One route: the open outlet is in the address (`?outlet=`), beside the list it was opened
645
645
  // from, so a link to it is a link to both.
646
- routes: [{ path: d, name: "webx.press", component: Xe, props: { base: d } }]
646
+ routes: [{ path: d, name: "webx.press", component: Ze, props: { base: d } }]
647
647
  };
648
648
  }
649
649
  export {
650
650
  Z as PRESS_API,
651
- Ee as WxOutletPane,
652
- Xe as WxPressPage,
651
+ Ue as WxOutletPane,
652
+ Ze as WxPressPage,
653
653
  de as createPressApi,
654
- at as press,
655
- Re as pressMessages
654
+ ot as press,
655
+ ze as pressMessages
656
656
  };
657
657
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/api.ts","../src/messages.ts","../src/i18n.ts","../src/OutletPane.vue","../src/PressPage.vue","../src/module.ts"],"sourcesContent":["import { reorderItems, type AdminContext } from '@webx-ui/module-admin'\nimport type { ScreenModel } from '@webx-ui/schema'\nimport type { OutletDetail, OutletQuery, OutletRow, OutletsList } from './types'\n\nexport interface PressApi {\n list(query?: OutletQuery): Promise<OutletsList>\n get(id: number): Promise<OutletDetail>\n /**\n * A new outlet out of the values of its form, articles included, in one request: the form is\n * filled in first and the record made on the first save, so nobody leaves an empty outlet behind.\n */\n create(values: ScreenModel): Promise<OutletDetail>\n /** Refused with a 422 under the name of the field — `articles.<n>.<field>` for an article. */\n save(id: number, values: ScreenModel): Promise<OutletDetail>\n remove(id: number): Promise<void>\n restore(id: number): Promise<OutletRow>\n /** The order of the whole list. The order of the articles is the order of the form's rows. */\n reorder(ids: number[]): Promise<void>\n}\n\n/** The path of the section under the panel's API, and of its order. */\nexport const PRESS_API = 'press'\n\n/** Everything under `/press`, below the panel's API path (§4.10). */\nexport function createPressApi(admin: AdminContext): PressApi {\n const base = `${admin.apiPath}/${PRESS_API}`\n const data = <T>(body: { data: T }): T => body.data\n\n return {\n list: (query = {}) => {\n const search = new URLSearchParams()\n\n if (query.search) search.set('search', query.search)\n if (query.trashed) search.set('trashed', '1')\n\n return admin.http.get<OutletsList>(search.size > 0 ? `${base}?${search}` : base)\n },\n get: (id) => admin.http.get<{ data: OutletDetail }>(`${base}/${id}`).then(data),\n create: (values) => admin.http.post<{ data: OutletDetail }>(base, { values }).then(data),\n save: (id, values) =>\n admin.http.put<{ data: OutletDetail }>(`${base}/${id}`, { values }).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 answer: a restored outlet stays closed.\n restore: (id) => admin.http.post<{ data: OutletRow }>(`${base}/${id}/restore`, {}).then(data),\n reorder: (ids) => reorderItems(admin, PRESS_API, ids, null),\n }\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-press::` and overrides these;\n * `messages.test.ts` keeps the two sets of keys equal.\n *\n * `screen` is what the described screen `press.outlet-form` is labelled with: the server sends the\n * labels as `trans::webx-press::screen.*`, and these are what they say when no dictionary came.\n *\n * `kinds` are the four of the default config (decision 3). A kind a site adds brings its word in\n * `lang/vendor/webx-press` — the server labels the options of the field itself, so a kind missing\n * here is only missing from a panel that runs with no server at all.\n */\nexport const pressMessages: Record<string, Messages> = {\n module: {\n press: 'Press',\n },\n outlet: {\n new: 'New outlet',\n untitled: 'Untitled',\n search: 'Search the outlets',\n empty: 'No outlets yet.',\n 'empty-help':\n 'An outlet is a magazine, a paper or a site that wrote about you; its articles are added on its form.',\n 'empty-search': 'Nothing matches that.',\n 'empty-bin': 'The bin is empty.',\n all: 'All',\n trashed: 'Bin',\n 'not-published': 'Not published',\n 'visible-nowhere': 'Published, but seen in no language: none of its articles has a title yet.',\n 'articles-count': 'Articles: :count',\n 'no-articles': 'No articles yet',\n featured: 'In the logo strip',\n 'seen-in': 'Seen in: :locales',\n choose: 'Choose an outlet',\n 'choose-help': 'Or add a new one: the first line of the list.',\n save: 'Save',\n saved: 'The outlet is saved.',\n 'save-failed': 'The outlet was not saved.',\n cancel: 'Cancel',\n delete: 'Delete',\n 'delete-title': 'Delete “:name”?',\n 'delete-text':\n 'It goes to the bin with its articles and leaves the site and every block. Restored, it comes back with them.',\n deleted: 'The outlet is in the bin.',\n restore: 'Restore',\n restored: 'The outlet is back.',\n 'reorder-failed': 'The new order was not saved.',\n 'leave-title': 'Leave without saving?',\n 'leave-text': 'What you wrote in this outlet is not on the server.',\n leave: 'Leave',\n back: 'Back to the list',\n open: 'Open on the site',\n 'address-moving': 'The page of the outlet moves to the new address when you save.',\n },\n screen: {\n general: 'General',\n outlet: 'Outlet',\n logo: 'Logo',\n 'logo-help': 'Without a logo the site prints the name.',\n title: 'Name',\n 'title-help': 'Where it is not written in a language, the name from another one is shown.',\n slug: 'Address',\n 'slug-help':\n \"The outlet's page. A language with articles and no address of its own takes this one.\",\n 'website-url': 'Website',\n 'website-url-help': 'An address that starts with http:// or https://.',\n summary: 'About the outlet',\n 'summary-help':\n 'A few words under the name on its page, and its description for search engines.',\n settings: 'Settings',\n published: 'Published',\n 'published-help': 'On the site at once — in the languages it has articles in.',\n featured: 'In the strip of logos',\n 'featured-help': 'Shown by the logos block set to the marked outlets only.',\n articles: 'Articles',\n 'articles-help': \"In the order they are shown on the outlet's page. Saved with the outlet.\",\n 'article-label': '#:number · :title',\n 'article-untitled': 'Untitled',\n 'article-title': 'Title',\n 'article-title-help': 'An article is shown in a language only when it has a title in it.',\n excerpt: 'Excerpt',\n kind: 'Kind',\n 'kind-none': 'No kind',\n 'published-on': 'Date',\n 'date-precision': 'Known to',\n 'date-precision-help': 'How much of the date is printed: the day, the month, or the year only.',\n precision: {\n day: 'Day',\n month: 'Month',\n year: 'Year',\n },\n url: 'Link',\n 'url-help': \"The article on the outlet's site. Without it the title leads to the PDF.\",\n file: 'PDF',\n 'file-help': 'A PDF from the library — a scan of the page, say.',\n hidden: 'Do not show',\n 'hidden-help': 'Kept here, and shown nowhere on the site.',\n seo: 'SEO',\n 'seo-empty': 'The SEO card appears here when module-seo is installed.',\n },\n kinds: {\n mention: 'Mention',\n interview: 'Interview',\n expert_comment: 'Expert comment',\n authored: 'Authored article',\n },\n}\n","import { useAdmin } from '@webx-ui/module-admin'\nimport { pressMessages } 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 usePressMessages(): void {\n try {\n const { i18n } = useAdmin()\n\n if (!seeded.has(i18n)) {\n i18n.defaults('webx-press', pressMessages)\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 } from 'vue'\nimport { onBeforeRouteLeave, onBeforeRouteUpdate, type RouteLocationNormalized } from 'vue-router'\nimport {\n confirm,\n localizedValue,\n toast,\n useLocales,\n WxActionBar,\n WxBadge,\n WxButton,\n WxSkeleton,\n type LocalizedValue,\n} from '@webx-ui/core'\nimport {\n provideRecordAddress,\n useAdmin,\n useErrorText,\n useTranslate,\n WxSaveState,\n WxScreen,\n WxScreenHead,\n type ScreenAction,\n} from '@webx-ui/module-admin'\nimport type { ScreenModel } from '@webx-ui/schema'\nimport { createPressApi } from './api'\nimport { usePressMessages } from './i18n'\nimport type { OutletDetail, OutletSummary } from './types'\n\n/**\n * One outlet with its articles: the pane beside the list, and the whole screen on a phone.\n *\n * The described screen `press.outlet-form` — General · Articles · SEO — with a head over it and\n * one button under it. One explicit save for the whole outlet, articles included (decision 15):\n * the articles are rows of its form, and the server writes them into their own table in the same\n * transaction (§4.6). No autosave: there is no draft (decision 9), so what is saved is on the site\n * at once — the button is the moment somebody decides it is ready.\n *\n * A new outlet is a form first and a record on the first save: an empty row made by pressing\n * \"New outlet\" and walking away would be an outlet with nothing in it on the list for ever.\n *\n * The way back is drawn here and not by the pane around it: the drawer a narrow screen opens\n * this in has no close of its own on purpose (CLAUDE.md §4).\n */\ndefineOptions({ name: 'WxOutletPane' })\n\nconst props = withDefaults(\n defineProps<{\n /** `null` — an outlet not written yet. */\n id: number | null\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 /** The outlet was written; the list redraws its row. */\n saved: [outlet: OutletSummary]\n /** The first save of a new outlet: it is a record now, with an id to be opened by. */\n created: [outlet: OutletSummary]\n removed: [id: number]\n}>()\n\nconst context = useAdmin()\nconst api = createPressApi(context)\nconst locales = useLocales()\nusePressMessages()\n\nconst t = useTranslate('webx-press')\n/* Not the server's `message`: the panel says how a request failed in its own words. */\nconst message = useErrorText()\n\nconst outlet = ref<OutletSummary | null>(null)\nconst values = ref<ScreenModel>({})\nconst prefix = ref<string | null>(null)\nconst snapshot = ref('')\n\nconst loading = ref(props.id !== null)\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\n\nconst canManage = computed(() => context.can('press.manage'))\n\nconst current = computed(() => JSON.stringify(values.value))\nconst dirty = computed(() => snapshot.value !== '' && current.value !== snapshot.value)\n\nconst state = computed<'saving' | 'unsaved' | 'saved'>(() => {\n if (saving.value) return 'saving'\n\n return dirty.value || props.id === null ? 'unsaved' : 'saved'\n})\n\n/** The name 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 = props.id === null ? t('outlet.new') : (outlet.value?.title ?? '')\n\n return localizedValue(written, locales.active.value, fallback) || t('outlet.untitled')\n})\n\n/*\n * The address field is the panel's shared one (`wx-slug`), and this is what it prints. The form\n * answers with the page's address (`url`) in the panel's language — `/en/press/…` on a site whose\n * default is another — while the field compares it with the slug in the language being edited.\n * So the address is handed over without the host, the slash and the language, and only in its own\n * language: in any other the field has nothing to compare with, and says nothing rather than\n * warning that a page moves which does not.\n */\nprovideRecordAddress({\n values,\n prefix,\n path: computed(() => pathIn(outlet.value?.url ?? null, locales.active.value)),\n moving: () => t('outlet.address-moving'),\n})\n\nfunction pathIn(url: string | null, locale: string): string | null | undefined {\n if (url === null) return null\n\n let segments: string[]\n\n try {\n segments = new URL(url, 'http://site.invalid').pathname.split('/').filter((one) => one !== '')\n } catch {\n return null\n }\n\n const codes = locales.list.value.map((one) => one.code)\n const own =\n codes.includes(segments[0] ?? '') && segments[0] !== codes[0] ? segments.shift()! : codes[0]\n\n return own === undefined || own === locale ? segments.join('/') : undefined\n}\n\nfunction take(detail: OutletDetail): void {\n outlet.value = detail.outlet\n values.value = detail.values\n prefix.value = detail.prefix\n snapshot.value = JSON.stringify(detail.values)\n}\n\nif (props.id === null) {\n // What the server would answer for an outlet with nothing in it yet.\n const blank: ScreenModel = {\n title: {},\n slug: {},\n summary: {},\n published: false,\n featured: false,\n articles: [],\n }\n\n values.value = blank\n snapshot.value = JSON.stringify(blank)\n}\n\nasync function load(): Promise<void> {\n if (props.id === null) return\n\n loading.value = true\n\n try {\n take(await api.get(props.id))\n } catch (error) {\n toast.danger(message(error))\n emit('back')\n } finally {\n loading.value = false\n }\n}\n\nasync function save(): Promise<void> {\n if (saving.value || !canManage.value) return\n\n saving.value = true\n errors.value = {}\n\n try {\n const made = props.id === null\n const detail = made\n ? await api.create(values.value)\n : await api.save(props.id as number, values.value)\n\n take(detail)\n toast.success(t('outlet.saved'))\n if (made) emit('created', detail.outlet)\n else emit('saved', detail.outlet)\n } catch (error) {\n const body = (error as { body?: { errors?: Record<string, string[]> } }).body\n\n if (body?.errors) {\n // `articles.2.url` as the server names it: the repeater finds its row and field by that.\n errors.value = body.errors\n toast.danger(t('outlet.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 = outlet.value\n\n if (!record) return\n\n const agreed = await confirm({\n title: t('outlet.delete-title', { name: record.title }),\n message: t('outlet.delete-text'),\n confirmText: t('outlet.delete'),\n cancelText: t('outlet.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.remove(record.id)\n toast.success(t('outlet.deleted'))\n // Nothing left to protect: the record is in the bin.\n snapshot.value = current.value\n emit('removed', record.id)\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 || props.id === null) void save()\n }\n}\n\nonMounted(() => {\n void load()\n window.addEventListener('beforeunload', leaveGuard)\n})\n\nonBeforeUnmount(() => {\n window.removeEventListener('beforeunload', leaveGuard)\n})\n\nasync function mayLeave(): Promise<boolean> {\n if (!dirty.value || !canManage.value) return true\n\n return await confirm({\n title: t('outlet.leave-title'),\n message: t('outlet.leave-text'),\n confirmText: t('outlet.leave'),\n cancelText: t('outlet.cancel'),\n tone: 'danger',\n })\n}\n\n/*\n * The list and this form are one route, so another outlet is an update of it rather than a way\n * out — and so is every keystroke in the search above the list, which goes into the address too.\n * Only a change of the outlet asks.\n */\nonBeforeRouteUpdate(async (to: RouteLocationNormalized, from: RouteLocationNormalized) =>\n to.query.outlet === from.query.outlet ? true : await mayLeave(),\n)\n\nonBeforeRouteLeave(mayLeave)\n\n/* What leads away from the outlet. On a narrow pane the head folds them into the ···. */\nconst actions = computed<ScreenAction[]>(() => {\n const record = outlet.value\n const list: ScreenAction[] = []\n\n // Only a page that answers: an unpublished outlet, or one seen nowhere, has no address.\n if (record?.url) {\n const url = record.url\n\n list.push({\n key: 'open',\n label: t('outlet.open'),\n icon: 'external-link',\n run: () => void window.open(url, '_blank', 'noopener'),\n })\n }\n\n if (canManage.value && record !== null) {\n list.push({\n key: 'delete',\n label: t('outlet.delete'),\n icon: 'trash',\n danger: true,\n menu: true,\n run: () => void remove(),\n })\n }\n\n return list\n})\n</script>\n\n<template>\n <div class=\"wx-outlet\" @keydown=\"onKeydown\">\n <wx-skeleton v-if=\"loading\" class=\"wx-outlet__ghost\" title :rows=\"6\" />\n\n <template v-else>\n <wx-screen-head\n :level=\"3\"\n :back=\"!props.inline\"\n :back-label=\"t('outlet.back')\"\n :title=\"title\"\n :actions=\"actions\"\n :collapse-below=\"480\"\n @back=\"emit('back')\"\n >\n <template v-if=\"outlet && !outlet.published\" #title-after>\n <wx-badge dot>{{ t('outlet.not-published') }}</wx-badge>\n </template>\n </wx-screen-head>\n\n <wx-screen\n v-model=\"values\"\n name=\"press.outlet-form\"\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 && props.id !== null\"\n @click=\"save\"\n >\n {{ t('outlet.save') }}\n </wx-button>\n </wx-action-bar>\n </template>\n </div>\n</template>\n\n<style scoped>\n.wx-outlet {\n display: flex;\n flex-direction: column;\n gap: var(--wx-gap, var(--wx-space-16));\n min-width: 0;\n padding: var(--wx-gap, var(--wx-space-16));\n container-type: inline-size;\n}\n\n.wx-outlet__ghost {\n max-width: 480px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, ref, watch } from 'vue'\nimport { useRoute, useRouter, type LocationQueryRaw } from 'vue-router'\nimport {\n confirm,\n toast,\n WxAlert,\n WxAvatar,\n WxBadge,\n WxCard,\n WxEmpty,\n WxIcon,\n WxInput,\n WxListDetail,\n WxSkeleton,\n WxSortableList,\n WxTooltip,\n type TabItem,\n type TabValue,\n} from '@webx-ui/core'\nimport {\n useAdmin,\n useErrorText,\n useItemOrder,\n useTranslate,\n WxListScreen,\n WxRowMenu,\n type RowAction,\n} from '@webx-ui/module-admin'\nimport { createPressApi, PRESS_API } from './api'\nimport { usePressMessages } from './i18n'\nimport OutletPane from './OutletPane.vue'\nimport type { OutletRow, OutletSummary } from './types'\n\n/**\n * The outlets: all of them on the left, the one being written on the right (§4.9, decision 15).\n *\n * The shape of the reviews, for the same reason: an outlet is typed in from a clipping in a\n * minute, and the next one is usually under it. On a phone the form is the whole screen, in the\n * drawer `WxListDetail` raises, and draws its own way back.\n *\n * One order, the general one (decision 6): a drag moves it; a search or the bin shows a\n * selection, so the grips go away and the line under the list says why. The order of the\n * articles is not here — it is the order of the rows in the outlet's form.\n *\n * Everything the screen is looking at lives in the address — the view, the search and the open\n * outlet — so a link to an outlet is a link to it in its list.\n */\nwithDefaults(defineProps<{ base?: string }>(), { base: '/press' })\n\nconst context = useAdmin()\nconst api = createPressApi(context)\nconst route = useRoute()\nconst router = useRouter()\nusePressMessages()\n\nconst t = useTranslate('webx-press')\n/* Not the server's `message`: the panel says how a request failed in its own words. */\nconst message = useErrorText()\n\nconst rows = ref<OutletRow[]>([])\nconst loading = ref(true)\n\nconst canManage = computed(() => context.can('press.manage'))\n\nconst title = computed(\n () =>\n context.state.manifest?.modules.find((module) => module.id === 'press')?.title ??\n t('module.press'),\n)\n\nconst view = computed<TabValue>({\n get: () => (route.query.view === 'trashed' ? 'trashed' : ''),\n set: (value) => {\n const query: LocationQueryRaw = {\n ...route.query,\n view: value === '' ? undefined : String(value),\n }\n\n // An outlet from the list is not an outlet from the bin: the form closes with the view.\n delete query.outlet\n void router.replace({ query })\n },\n})\n\nconst inBin = computed(() => view.value === 'trashed')\n\nconst q = computed(() => (typeof route.query.q === 'string' ? route.query.q : ''))\n\n/* What is typed, ahead of the address: the address follows it after a pause. */\nconst typed = ref(q.value)\n\n/** The open outlet: a number, `'new'` for one not yet saved, or nothing. */\nconst selected = computed<number | 'new' | null>(() => {\n const raw = route.query.outlet\n\n if (raw === 'new') return canManage.value ? 'new' : null\n\n return typeof raw === 'string' && raw !== '' ? Number(raw) || null : null\n})\n\n/*\n * Open is \"an outlet is chosen\", and closing is un-choosing it — so the address, the list's\n * highlight and the drawer on a phone can never disagree about it.\n */\nconst open = computed({\n get: () => selected.value !== null,\n set: (value) => {\n if (!value) close()\n },\n})\n\nconst views = computed<TabItem[]>(() => [\n { value: '', label: t('outlet.all') },\n { value: 'trashed', label: t('outlet.trashed'), icon: 'trash' },\n])\n\nconst order = useItemOrder(PRESS_API, () => ({ q: q.value, filtered: inBin.value }))\n\n/* Dragged only by somebody who may write, and only in a list that is the whole order. */\nconst sortable = computed(() => canManage.value && order.sortable.value)\n\nconst emptyText = computed(() => {\n if (q.value !== '') return t('outlet.empty-search')\n\n return inBin.value ? t('outlet.empty-bin') : t('outlet.empty')\n})\n\nasync function load(): Promise<void> {\n loading.value = true\n\n try {\n const answer = await api.list({ search: q.value, trashed: inBin.value })\n\n rows.value = answer.data\n } catch (error) {\n toast.danger(message(error))\n } finally {\n loading.value = false\n }\n}\n\nwatch(\n () => [view.value, q.value] as const,\n () => void load(),\n)\nvoid load()\n\nlet pause: ReturnType<typeof setTimeout> | undefined\n\nwatch(typed, (value) => {\n clearTimeout(pause)\n pause = setTimeout(() => {\n void router.replace({ query: { ...route.query, q: value.trim() === '' ? undefined : value } })\n }, 300)\n})\n\nonBeforeUnmount(() => clearTimeout(pause))\n\n/* `replace` and not `push`: going through twenty outlets is not twenty steps of history. */\nfunction choose(id: number | 'new'): void {\n void router.replace({ query: { ...route.query, outlet: String(id) } })\n}\n\nfunction close(): void {\n const query = { ...route.query }\n\n delete query.outlet\n void router.replace({ query })\n}\n\n/** A new outlet was saved: it is a record now, and the list has it. */\nfunction created(outlet: OutletSummary): void {\n choose(outlet.id)\n void load()\n}\n\n/**\n * The order on screen, written as a whole. The list is already in its new order\n * (`WxSortableList` reorders the model before it says so), so a failure has to put it back\n * rather than leave the screen disagreeing with the database.\n */\nasync function reorder(): Promise<void> {\n try {\n await order.move(rows.value.map((row) => row.id))\n } catch (error) {\n toast.danger(message(error, t('outlet.reorder-failed')))\n await load()\n }\n}\n\nfunction actionsFor(row: OutletRow): RowAction[] {\n if (!canManage.value) return []\n\n if (inBin.value) {\n return [\n {\n key: 'restore',\n icon: 'refresh',\n label: t('outlet.restore'),\n run: () => void restore(row),\n },\n ]\n }\n\n return [\n {\n key: 'delete',\n icon: 'trash',\n label: t('outlet.delete'),\n danger: true,\n run: () => void remove(row),\n },\n ]\n}\n\nasync function remove(row: OutletRow): Promise<void> {\n const agreed = await confirm({\n title: t('outlet.delete-title', { name: row.title }),\n message: t('outlet.delete-text'),\n confirmText: t('outlet.delete'),\n cancelText: t('outlet.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.remove(row.id)\n toast.success(t('outlet.deleted'))\n removed(row.id)\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\n/** Gone from the list; and if it was the open one, the form goes with it. */\nfunction removed(id: number): void {\n if (selected.value === id) close()\n\n void load()\n}\n\nasync function restore(row: OutletRow): Promise<void> {\n try {\n await api.restore(row.id)\n toast.success(t('outlet.restored'))\n } catch (error) {\n toast.danger(message(error))\n } finally {\n await load()\n }\n}\n\n/* Published and yet shown in no language: the one state the list has to point out. */\nfunction unseen(row: OutletRow): boolean {\n return row.published && row.locales.length === 0\n}\n</script>\n\n<template>\n <wx-list-screen v-model:view=\"view\" :title=\"title\" :views=\"views\" :card=\"false\">\n <wx-card class=\"wx-press\" padding=\"none\">\n <wx-list-detail\n v-model:open=\"open\"\n class=\"wx-press__panes\"\n :list-width=\"360\"\n :detail-min=\"520\"\n :detail-label=\"t('module.press')\"\n >\n <template #list>\n <div class=\"wx-press__bar\">\n <wx-input\n v-model=\"typed\"\n class=\"wx-press__search\"\n size=\"sm\"\n clearable\n :placeholder=\"t('outlet.search')\"\n :aria-label=\"t('outlet.search')\"\n >\n <template #prefix><wx-icon name=\"search\" size=\"sm\" /></template>\n </wx-input>\n </div>\n\n <!--\n A line of the list rather than a button in the head: the new outlet is written where\n it will stand, beside the ones it joins — and the form opens the way any other does.\n -->\n <button\n v-if=\"canManage && !inBin\"\n type=\"button\"\n class=\"wx-press__new\"\n :class=\"{ 'is-current': selected === 'new' }\"\n @click=\"choose('new')\"\n >\n <wx-icon name=\"plus\" size=\"sm\" />\n {{ t('outlet.new') }}\n </button>\n\n <wx-skeleton v-if=\"loading && rows.length === 0\" class=\"wx-press__loading\" :rows=\"5\" />\n\n <wx-empty\n v-else-if=\"rows.length === 0\"\n :title=\"emptyText\"\n :description=\"!inBin && q === '' ? t('outlet.empty-help') : undefined\"\n />\n\n <template v-else>\n <!-- A grip and not the whole row: the row opens an outlet, and a row that both opens\n and drags is one where one of the two happens by accident. -->\n <wx-sortable-list\n v-model=\"rows\"\n class=\"wx-press__list\"\n :class=\"{ 'is-loading': loading }\"\n plain\n item-key=\"id\"\n :handle=\"sortable ? 'grip' : 'row'\"\n :item-label=\"(item: OutletRow) => item.title\"\n :disabled=\"!sortable\"\n @move=\"reorder\"\n >\n <template #default=\"{ item }\">\n <component\n :is=\"inBin ? 'div' : 'button'\"\n :type=\"inBin ? undefined : 'button'\"\n class=\"wx-outlet-row\"\n :class=\"{ 'is-current': selected === item.id }\"\n @click=\"inBin ? undefined : choose(item.id)\"\n >\n <!-- A logo is wide as often as not: in a square it shrinks to a sliver, so it gets a\n box of its own shape, and only an outlet without one gets initials. -->\n <span v-if=\"item.logo?.thumb\" class=\"wx-outlet-row__logo\">\n <img :src=\"item.logo.thumb\" alt=\"\" />\n </span>\n <wx-avatar\n v-else\n class=\"wx-outlet-row__initials\"\n shape=\"square\"\n :name=\"item.title\"\n size=\"md\"\n />\n\n <span class=\"wx-outlet-row__body\">\n <span class=\"wx-outlet-row__head\">\n <span class=\"wx-outlet-row__title\">{{ item.title }}</span>\n <wx-tooltip v-if=\"item.featured\" :content=\"t('outlet.featured')\">\n <wx-icon\n class=\"wx-outlet-row__featured\"\n name=\"star\"\n size=\"sm\"\n :aria-label=\"t('outlet.featured')\"\n />\n </wx-tooltip>\n </span>\n\n <span class=\"wx-outlet-row__facts\">\n <wx-badge v-if=\"!item.published\" dot size=\"sm\">\n {{ t('outlet.not-published') }}\n </wx-badge>\n <wx-tooltip v-else-if=\"unseen(item)\" :content=\"t('outlet.visible-nowhere')\">\n <wx-badge type=\"warning\" dot size=\"sm\">\n {{ t('outlet.not-published') }}\n </wx-badge>\n </wx-tooltip>\n\n <span class=\"wx-outlet-row__count\">\n {{\n item.articles_count > 0\n ? t('outlet.articles-count', { count: item.articles_count })\n : t('outlet.no-articles')\n }}\n </span>\n\n <span\n v-if=\"item.locales.length > 0\"\n class=\"wx-outlet-row__locales\"\n :aria-label=\"t('outlet.seen-in', { locales: item.locales.join(', ') })\"\n >\n {{ item.locales.join(' · ') }}\n </span>\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, or why there is nothing to drag. -->\n <wx-alert\n v-if=\"canManage && !inBin\"\n class=\"wx-press__note\"\n type=\"info\"\n :description=\"order.hint.value\"\n />\n </template>\n </template>\n\n <template #detail=\"{ inline, back }\">\n <outlet-pane\n v-if=\"selected !== null\"\n :id=\"selected === 'new' ? null : selected\"\n :key=\"String(selected)\"\n :inline=\"inline\"\n @back=\"back\"\n @saved=\"load\"\n @created=\"created\"\n @removed=\"removed\"\n />\n </template>\n\n <template #empty>\n <wx-empty\n :title=\"t('outlet.choose')\"\n :description=\"canManage ? t('outlet.choose-help') : undefined\"\n />\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. The card's\n * corners are the screen's, and the panes paint their own background to the edge: `clip`, not\n * `hidden`, which would make this a scroll container and pin everything sticky inside to a box\n * that never moves (CLAUDE.md §4).\n */\n.wx-press > .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-press__bar {\n display: flex;\n gap: var(--wx-space-8);\n padding: var(--wx-space-12) var(--wx-space-12) var(--wx-space-8);\n}\n\n.wx-press__search {\n flex: 1 1 180px;\n min-width: 0;\n}\n\n.wx-press__new {\n display: flex;\n align-items: center;\n gap: var(--wx-space-8);\n width: calc(100% - 2 * var(--wx-space-8));\n margin: 0 var(--wx-space-8) var(--wx-space-4);\n padding: var(--wx-space-8);\n border: 1px dashed var(--wx-border-default);\n border-radius: var(--wx-radius-sm);\n background: none;\n font: inherit;\n color: var(--wx-color-primary);\n text-align: start;\n cursor: pointer;\n}\n\n.wx-press__new:hover,\n.wx-press__new.is-current {\n background: var(--wx-bg-subtle);\n}\n\n.wx-press__loading {\n padding: var(--wx-space-12);\n}\n\n.wx-press__list {\n padding-inline: var(--wx-space-8);\n}\n\n.wx-press__list.is-loading {\n opacity: 0.6;\n}\n\n.wx-press__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-press__list :deep(.wx-sortable-list__row:hover),\n.wx-press__list :deep(.wx-sortable-list__row:has(.is-current)) {\n background: var(--wx-bg-subtle);\n}\n\n.wx-press__note {\n margin: var(--wx-space-8);\n}\n\n.wx-outlet-row {\n display: flex;\n align-items: flex-start;\n gap: var(--wx-space-10);\n /* The row is the target: a short name should not leave half the line dead — and a button,\n unlike a link, is only as wide as what is in it until it is told otherwise. */\n flex: 1 1 auto;\n width: 100%;\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}\n\nbutton.wx-outlet-row {\n cursor: pointer;\n}\n\n.wx-outlet-row__initials {\n flex: none;\n}\n\n/* Twice as wide as the initials beside it, and the logo drawn whole inside: cropped, it loses\n its letters. */\n.wx-outlet-row__logo {\n display: flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 64px;\n height: 32px;\n overflow: hidden;\n border-radius: var(--wx-radius-sm);\n background: var(--wx-bg-subtle);\n}\n\n.wx-outlet-row__logo img {\n display: block;\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n}\n\n.wx-outlet-row__body {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n gap: var(--wx-space-2);\n min-width: 0;\n}\n\n.wx-outlet-row__head {\n display: flex;\n align-items: center;\n gap: var(--wx-space-6);\n min-width: 0;\n}\n\n.wx-outlet-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\n.wx-outlet-row.is-current .wx-outlet-row__title {\n color: var(--wx-color-primary);\n}\n\n.wx-outlet-row__featured {\n flex: none;\n color: var(--wx-color-warning);\n}\n\n.wx-outlet-row__facts {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--wx-space-4) var(--wx-space-8);\n min-width: 0;\n color: var(--wx-text-muted);\n font-size: var(--wx-font-size-sm);\n}\n\n.wx-outlet-row__locales {\n font-family: var(--wx-font-family-mono);\n font-size: var(--wx-font-size-xs);\n text-transform: uppercase;\n}\n</style>\n","import type { AdminModule } from '@webx-ui/module-admin'\nimport PressPage from './PressPage.vue'\n\nexport interface PressOptions {\n /** Where the outlets live inside the panel. */\n path?: string\n}\n\n/**\n * The press as a section of the panel (§4.9): one module and one entry, without a group of its\n * own — the reviews have one because they have two entries, and a group around a single item is\n * a heading over itself. A section whose server half is not installed never appears — the entry\n * is built from the manifest.\n */\nexport function press(options: PressOptions = {}): AdminModule {\n const path = options.path ?? '/press'\n\n return {\n id: 'press',\n path,\n // One route: the open outlet is in the address (`?outlet=`), beside the list it was opened\n // from, so a link to it is a link to both.\n routes: [{ path, name: 'webx.press', component: PressPage, props: { base: path } }],\n }\n}\n"],"names":["PRESS_API","createPressApi","admin","base","data","body","query","search","id","values","ids","reorderItems","pressMessages","seeded","usePressMessages","i18n","useAdmin","props","__props","emit","__emit","context","api","locales","useLocales","t","useTranslate","message","useErrorText","outlet","ref","prefix","snapshot","loading","saving","errors","canManage","computed","current","dirty","state","title","written","fallback","localizedValue","provideRecordAddress","pathIn","url","locale","segments","one","codes","own","take","detail","blank","load","error","toast","save","made","remove","record","confirm","leaveGuard","event","onKeydown","onMounted","onBeforeUnmount","mayLeave","onBeforeRouteUpdate","to","from","onBeforeRouteLeave","actions","list","_createElementBlock","_createBlock","_unref","WxSkeleton","_Fragment","_createVNode","WxScreenHead","WxBadge","WxScreen","$event","WxActionBar","WxSaveState","WxButton","route","useRoute","router","useRouter","rows","module","view","value","inBin","q","typed","selected","raw","open","close","views","order","useItemOrder","sortable","emptyText","answer","watch","pause","choose","created","reorder","row","actionsFor","restore","removed","unseen","WxListScreen","WxCard","WxListDetail","_createElementVNode","_hoisted_1","WxInput","WxIcon","_normalizeClass","_createTextVNode","WxEmpty","WxSortableList","item","_withCtx","_openBlock","_resolveDynamicComponent","_hoisted_2","WxAvatar","_hoisted_4","_hoisted_5","_hoisted_6","_toDisplayString","WxTooltip","_hoisted_7","_hoisted_8","_hoisted_9","WxRowMenu","WxAlert","inline","back","OutletPane","press","options","path","PressPage"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/api.ts","../src/messages.ts","../src/i18n.ts","../src/OutletPane.vue","../src/PressPage.vue","../src/module.ts"],"sourcesContent":["import { reorderItems, type AdminContext } from '@webx-ui/module-admin'\nimport type { ScreenModel } from '@webx-ui/schema'\nimport type { OutletDetail, OutletQuery, OutletRow, OutletsList } from './types'\n\nexport interface PressApi {\n list(query?: OutletQuery): Promise<OutletsList>\n get(id: number): Promise<OutletDetail>\n /**\n * A new outlet out of the values of its form, articles included, in one request: the form is\n * filled in first and the record made on the first save, so nobody leaves an empty outlet behind.\n */\n create(values: ScreenModel): Promise<OutletDetail>\n /** Refused with a 422 under the name of the field — `articles.<n>.<field>` for an article. */\n save(id: number, values: ScreenModel): Promise<OutletDetail>\n remove(id: number): Promise<void>\n restore(id: number): Promise<OutletRow>\n /** The order of the whole list. The order of the articles is the order of the form's rows. */\n reorder(ids: number[]): Promise<void>\n}\n\n/** The path of the section under the panel's API, and of its order. */\nexport const PRESS_API = 'press'\n\n/** Everything under `/press`, below the panel's API path (§4.10). */\nexport function createPressApi(admin: AdminContext): PressApi {\n const base = `${admin.apiPath}/${PRESS_API}`\n const data = <T>(body: { data: T }): T => body.data\n\n return {\n list: (query = {}) => {\n const search = new URLSearchParams()\n\n if (query.search) search.set('search', query.search)\n if (query.trashed) search.set('trashed', '1')\n\n return admin.http.get<OutletsList>(search.size > 0 ? `${base}?${search}` : base)\n },\n get: (id) => admin.http.get<{ data: OutletDetail }>(`${base}/${id}`).then(data),\n create: (values) => admin.http.post<{ data: OutletDetail }>(base, { values }).then(data),\n save: (id, values) =>\n admin.http.put<{ data: OutletDetail }>(`${base}/${id}`, { values }).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 answer: a restored outlet stays closed.\n restore: (id) => admin.http.post<{ data: OutletRow }>(`${base}/${id}/restore`, {}).then(data),\n reorder: (ids) => reorderItems(admin, PRESS_API, ids, null),\n }\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-press::` and overrides these;\n * `messages.test.ts` keeps the two sets of keys equal.\n *\n * `screen` is what the described screen `press.outlet-form` is labelled with: the server sends the\n * labels as `trans::webx-press::screen.*`, and these are what they say when no dictionary came.\n *\n * `kinds` are the four of the default config (decision 3). A kind a site adds brings its word in\n * `lang/vendor/webx-press` — the server labels the options of the field itself, so a kind missing\n * here is only missing from a panel that runs with no server at all.\n */\nexport const pressMessages: Record<string, Messages> = {\n module: {\n press: 'Press',\n },\n outlet: {\n new: 'New outlet',\n untitled: 'Untitled',\n search: 'Search the outlets',\n empty: 'No outlets yet.',\n 'empty-help':\n 'An outlet is a magazine, a paper or a site that wrote about you; its articles are added on its form.',\n 'empty-search': 'Nothing matches that.',\n 'empty-bin': 'The bin is empty.',\n all: 'All',\n trashed: 'Bin',\n 'not-published': 'Not published',\n 'visible-nowhere': 'Published, but seen in no language: none of its articles has a title yet.',\n 'articles-count': 'Articles: :count',\n 'no-articles': 'No articles yet',\n featured: 'In the logo strip',\n 'seen-in': 'Seen in: :locales',\n choose: 'Choose an outlet',\n 'choose-help': 'Or add a new one: the first line of the list.',\n save: 'Save',\n saved: 'The outlet is saved.',\n 'save-failed': 'The outlet was not saved.',\n cancel: 'Cancel',\n delete: 'Delete',\n 'delete-title': 'Delete “:name”?',\n 'delete-text':\n 'It goes to the bin with its articles and leaves the site and every block. Restored, it comes back with them.',\n deleted: 'The outlet is in the bin.',\n restore: 'Restore',\n restored: 'The outlet is back.',\n 'reorder-failed': 'The new order was not saved.',\n 'leave-title': 'Leave without saving?',\n 'leave-text': 'What you wrote in this outlet is not on the server.',\n leave: 'Leave',\n back: 'Back to the list',\n open: 'Open on the site',\n 'address-moving': 'The page of the outlet moves to the new address when you save.',\n },\n screen: {\n general: 'General',\n outlet: 'Outlet',\n logo: 'Logo',\n 'logo-help': 'Without a logo the site prints the name.',\n title: 'Name',\n 'title-help': 'Where it is not written in a language, the name from another one is shown.',\n slug: 'Address',\n 'slug-help':\n \"The outlet's page. A language with articles and no address of its own takes this one.\",\n 'website-url': 'Website',\n 'website-url-help': 'An address that starts with http:// or https://.',\n summary: 'About the outlet',\n 'summary-help':\n 'A few words under the name on its page, and its description for search engines.',\n settings: 'Settings',\n published: 'Published',\n 'published-help': 'On the site at once — in the languages it has articles in.',\n featured: 'In the strip of logos',\n 'featured-help': 'Shown by the logos block set to the marked outlets only.',\n articles: 'Articles',\n 'articles-help': \"In the order they are shown on the outlet's page. Saved with the outlet.\",\n 'article-label': '#:number · :title',\n 'article-untitled': 'Untitled',\n 'article-title': 'Title',\n 'article-title-help': 'An article is shown in a language only when it has a title in it.',\n excerpt: 'Excerpt',\n kind: 'Kind',\n 'kind-none': 'No kind',\n 'published-on': 'Date',\n 'date-precision': 'Known to',\n 'date-precision-help': 'How much of the date is printed: the day, the month, or the year only.',\n precision: {\n day: 'Day',\n month: 'Month',\n year: 'Year',\n },\n url: 'Link',\n 'url-help': \"The article on the outlet's site. Without it the title leads to the PDF.\",\n file: 'PDF',\n 'file-help': 'A PDF from the library — a scan of the page, say.',\n hidden: 'Do not show',\n 'hidden-help': 'Kept here, and shown nowhere on the site.',\n seo: 'SEO',\n 'seo-empty': 'The SEO card appears here when module-seo is installed.',\n },\n kinds: {\n mention: 'Mention',\n interview: 'Interview',\n expert_comment: 'Expert comment',\n authored: 'Authored article',\n },\n}\n","import { useAdmin } from '@webx-ui/module-admin'\nimport { pressMessages } 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 usePressMessages(): void {\n try {\n const { i18n } = useAdmin()\n\n if (!seeded.has(i18n)) {\n i18n.defaults('webx-press', pressMessages)\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 } from 'vue'\nimport { onBeforeRouteLeave, onBeforeRouteUpdate, type RouteLocationNormalized } from 'vue-router'\nimport {\n confirm,\n localizedValue,\n toast,\n useLocales,\n WxActionBar,\n WxBadge,\n WxButton,\n WxSkeleton,\n type LocalizedValue,\n} from '@webx-ui/core'\nimport {\n provideRecordAddress,\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 { ScreenModel } from '@webx-ui/schema'\nimport { createPressApi } from './api'\nimport { usePressMessages } from './i18n'\nimport type { OutletDetail, OutletSummary } from './types'\n\n/**\n * One outlet with its articles: the pane beside the list, and the whole screen on a phone.\n *\n * The described screen `press.outlet-form` — General · Articles · SEO — with a head over it and\n * one button under it. One explicit save for the whole outlet, articles included (decision 15):\n * the articles are rows of its form, and the server writes them into their own table in the same\n * transaction (§4.6). No autosave: there is no draft (decision 9), so what is saved is on the site\n * at once — the button is the moment somebody decides it is ready.\n *\n * A new outlet is a form first and a record on the first save: an empty row made by pressing\n * \"New outlet\" and walking away would be an outlet with nothing in it on the list for ever.\n *\n * The way back is drawn here and not by the pane around it: the drawer a narrow screen opens\n * this in has no close of its own on purpose (CLAUDE.md §4).\n */\ndefineOptions({ name: 'WxOutletPane' })\n\nconst props = withDefaults(\n defineProps<{\n /** `null` — an outlet not written yet. */\n id: number | null\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 /** The outlet was written; the list redraws its row. */\n saved: [outlet: OutletSummary]\n /** The first save of a new outlet: it is a record now, with an id to be opened by. */\n created: [outlet: OutletSummary]\n removed: [id: number]\n}>()\n\nconst context = useAdmin()\nconst api = createPressApi(context)\nconst locales = useLocales()\nusePressMessages()\n\nconst t = useTranslate('webx-press')\n/* Not the server's `message`: the panel says how a request failed in its own words. */\nconst message = useErrorText()\n\nconst outlet = ref<OutletSummary | null>(null)\nconst values = ref<ScreenModel>({})\nconst prefix = ref<string | null>(null)\nconst snapshot = ref('')\n\nconst loading = ref(props.id !== null)\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\n\nconst canManage = computed(() => context.can('press.manage'))\n\nconst current = computed(() => JSON.stringify(values.value))\nconst dirty = computed(() => snapshot.value !== '' && current.value !== snapshot.value)\n\nconst state = computed<'saving' | 'unsaved' | 'saved'>(() => {\n if (saving.value) return 'saving'\n\n return dirty.value || props.id === null ? 'unsaved' : 'saved'\n})\n\n/** The name 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 = props.id === null ? t('outlet.new') : (outlet.value?.title ?? '')\n\n return localizedValue(written, locales.active.value, fallback) || t('outlet.untitled')\n})\n\n/*\n * The address field is the panel's shared one (`wx-slug`), and this is what it prints. The form\n * answers with the page's address (`url`) in the panel's language — `/en/press/…` on a site whose\n * default is another — while the field compares it with the slug in the language being edited.\n * So the address is handed over without the host, the slash and the language, and only in its own\n * language: in any other the field has nothing to compare with, and says nothing rather than\n * warning that a page moves which does not.\n */\nprovideRecordAddress({\n values,\n prefix,\n path: computed(() => pathIn(outlet.value?.url ?? null, locales.active.value)),\n moving: () => t('outlet.address-moving'),\n})\n\nfunction pathIn(url: string | null, locale: string): string | null | undefined {\n if (url === null) return null\n\n let segments: string[]\n\n try {\n segments = new URL(url, 'http://site.invalid').pathname.split('/').filter((one) => one !== '')\n } catch {\n return null\n }\n\n const codes = locales.list.value.map((one) => one.code)\n const own =\n codes.includes(segments[0] ?? '') && segments[0] !== codes[0] ? segments.shift()! : codes[0]\n\n return own === undefined || own === locale ? segments.join('/') : undefined\n}\n\nfunction take(detail: OutletDetail): void {\n outlet.value = detail.outlet\n values.value = detail.values\n prefix.value = detail.prefix\n snapshot.value = JSON.stringify(detail.values)\n}\n\nif (props.id === null) {\n // What the server would answer for an outlet with nothing in it yet.\n const blank: ScreenModel = {\n title: {},\n slug: {},\n summary: {},\n published: false,\n featured: false,\n articles: [],\n }\n\n values.value = blank\n snapshot.value = JSON.stringify(blank)\n}\n\nasync function load(): Promise<void> {\n if (props.id === null) return\n\n loading.value = true\n\n try {\n take(await api.get(props.id))\n } catch (error) {\n toast.danger(message(error))\n emit('back')\n } finally {\n loading.value = false\n }\n}\n\nasync function save(): Promise<void> {\n if (saving.value || !canManage.value) return\n\n saving.value = true\n errors.value = {}\n\n try {\n const made = props.id === null\n const detail = made\n ? await api.create(values.value)\n : await api.save(props.id as number, values.value)\n\n take(detail)\n toast.success(t('outlet.saved'))\n if (made) emit('created', detail.outlet)\n else emit('saved', detail.outlet)\n } catch (error) {\n const body = (error as { body?: { errors?: Record<string, string[]> } }).body\n\n if (body?.errors) {\n // `articles.2.url` as the server names it: the repeater finds its row and field by that.\n errors.value = body.errors\n toast.danger(t('outlet.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 = outlet.value\n\n if (!record) return\n\n const agreed = await confirm({\n title: t('outlet.delete-title', { name: record.title }),\n message: t('outlet.delete-text'),\n confirmText: t('outlet.delete'),\n cancelText: t('outlet.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.remove(record.id)\n toast.success(t('outlet.deleted'))\n // Nothing left to protect: the record is in the bin.\n snapshot.value = current.value\n emit('removed', record.id)\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 || props.id === 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\nonMounted(() => {\n void load()\n window.addEventListener('beforeunload', leaveGuard)\n})\n\nonBeforeUnmount(() => {\n window.removeEventListener('beforeunload', leaveGuard)\n})\n\nasync function mayLeave(): Promise<boolean> {\n if (!dirty.value || !canManage.value) return true\n\n return await confirm({\n title: t('outlet.leave-title'),\n message: t('outlet.leave-text'),\n confirmText: t('outlet.leave'),\n cancelText: t('outlet.cancel'),\n tone: 'danger',\n })\n}\n\n/*\n * The list and this form are one route, so another outlet is an update of it rather than a way\n * out — and so is every keystroke in the search above the list, which goes into the address too.\n * Only a change of the outlet asks.\n */\nonBeforeRouteUpdate(async (to: RouteLocationNormalized, from: RouteLocationNormalized) =>\n to.query.outlet === from.query.outlet ? true : await mayLeave(),\n)\n\nonBeforeRouteLeave(mayLeave)\n\n/* What leads away from the outlet. On a narrow pane the head folds them into the ···. */\nconst actions = computed<ScreenAction[]>(() => {\n const record = outlet.value\n const list: ScreenAction[] = []\n\n // Only a page that answers: an unpublished outlet, or one seen nowhere, has no address.\n if (record?.url) {\n const url = record.url\n\n list.push({\n key: 'open',\n label: t('outlet.open'),\n icon: 'external-link',\n run: () => void window.open(url, '_blank', 'noopener'),\n })\n }\n\n if (canManage.value && record !== null) {\n list.push({\n key: 'delete',\n label: t('outlet.delete'),\n icon: 'trash',\n danger: true,\n menu: true,\n run: () => void remove(),\n })\n }\n\n return list\n})\n</script>\n\n<template>\n <div class=\"wx-outlet\" @keydown=\"onKeydown\">\n <wx-skeleton v-if=\"loading\" class=\"wx-outlet__ghost\" title :rows=\"6\" />\n\n <template v-else>\n <wx-screen-head\n :level=\"3\"\n :back=\"!props.inline\"\n :back-label=\"t('outlet.back')\"\n :title=\"title\"\n :actions=\"actions\"\n :collapse-below=\"480\"\n @back=\"emit('back')\"\n >\n <template v-if=\"outlet && !outlet.published\" #title-after>\n <wx-badge dot>{{ t('outlet.not-published') }}</wx-badge>\n </template>\n </wx-screen-head>\n\n <wx-screen\n v-model=\"values\"\n name=\"press.outlet-form\"\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 && props.id !== null\"\n @click=\"save\"\n >\n {{ t('outlet.save') }}\n </wx-button>\n </wx-action-bar>\n </template>\n </div>\n</template>\n\n<style scoped>\n.wx-outlet {\n display: flex;\n flex-direction: column;\n gap: var(--wx-gap, var(--wx-space-16));\n min-width: 0;\n padding: var(--wx-gap, var(--wx-space-16));\n container-type: inline-size;\n}\n\n.wx-outlet__ghost {\n max-width: 480px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, ref, watch } from 'vue'\nimport { useRoute, useRouter, type LocationQueryRaw } from 'vue-router'\nimport {\n confirm,\n toast,\n WxAlert,\n WxAvatar,\n WxBadge,\n WxCard,\n WxEmpty,\n WxIcon,\n WxInput,\n WxListDetail,\n WxSkeleton,\n WxSortableList,\n WxTooltip,\n type TabItem,\n type TabValue,\n} from '@webx-ui/core'\nimport {\n useAdmin,\n useErrorText,\n useItemOrder,\n useTranslate,\n WxListScreen,\n WxRowMenu,\n type RowAction,\n} from '@webx-ui/module-admin'\nimport { createPressApi, PRESS_API } from './api'\nimport { usePressMessages } from './i18n'\nimport OutletPane from './OutletPane.vue'\nimport type { OutletRow, OutletSummary } from './types'\n\n/**\n * The outlets: all of them on the left, the one being written on the right (§4.9, decision 15).\n *\n * The shape of the reviews, for the same reason: an outlet is typed in from a clipping in a\n * minute, and the next one is usually under it. On a phone the form is the whole screen, in the\n * drawer `WxListDetail` raises, and draws its own way back.\n *\n * One order, the general one (decision 6): a drag moves it; a search or the bin shows a\n * selection, so the grips go away and the line under the list says why. The order of the\n * articles is not here — it is the order of the rows in the outlet's form.\n *\n * Everything the screen is looking at lives in the address — the view, the search and the open\n * outlet — so a link to an outlet is a link to it in its list.\n */\nwithDefaults(defineProps<{ base?: string }>(), { base: '/press' })\n\nconst context = useAdmin()\nconst api = createPressApi(context)\nconst route = useRoute()\nconst router = useRouter()\nusePressMessages()\n\nconst t = useTranslate('webx-press')\n/* Not the server's `message`: the panel says how a request failed in its own words. */\nconst message = useErrorText()\n\nconst rows = ref<OutletRow[]>([])\nconst loading = ref(true)\n\nconst canManage = computed(() => context.can('press.manage'))\n\nconst title = computed(\n () =>\n context.state.manifest?.modules.find((module) => module.id === 'press')?.title ??\n t('module.press'),\n)\n\nconst view = computed<TabValue>({\n get: () => (route.query.view === 'trashed' ? 'trashed' : ''),\n set: (value) => {\n const query: LocationQueryRaw = {\n ...route.query,\n view: value === '' ? undefined : String(value),\n }\n\n // An outlet from the list is not an outlet from the bin: the form closes with the view.\n delete query.outlet\n void router.replace({ query })\n },\n})\n\nconst inBin = computed(() => view.value === 'trashed')\n\nconst q = computed(() => (typeof route.query.q === 'string' ? route.query.q : ''))\n\n/* What is typed, ahead of the address: the address follows it after a pause. */\nconst typed = ref(q.value)\n\n/** The open outlet: a number, `'new'` for one not yet saved, or nothing. */\nconst selected = computed<number | 'new' | null>(() => {\n const raw = route.query.outlet\n\n if (raw === 'new') return canManage.value ? 'new' : null\n\n return typeof raw === 'string' && raw !== '' ? Number(raw) || null : null\n})\n\n/*\n * Open is \"an outlet is chosen\", and closing is un-choosing it — so the address, the list's\n * highlight and the drawer on a phone can never disagree about it.\n */\nconst open = computed({\n get: () => selected.value !== null,\n set: (value) => {\n if (!value) close()\n },\n})\n\nconst views = computed<TabItem[]>(() => [\n { value: '', label: t('outlet.all') },\n { value: 'trashed', label: t('outlet.trashed'), icon: 'trash' },\n])\n\nconst order = useItemOrder(PRESS_API, () => ({ q: q.value, filtered: inBin.value }))\n\n/* Dragged only by somebody who may write, and only in a list that is the whole order. */\nconst sortable = computed(() => canManage.value && order.sortable.value)\n\nconst emptyText = computed(() => {\n if (q.value !== '') return t('outlet.empty-search')\n\n return inBin.value ? t('outlet.empty-bin') : t('outlet.empty')\n})\n\nasync function load(): Promise<void> {\n loading.value = true\n\n try {\n const answer = await api.list({ search: q.value, trashed: inBin.value })\n\n rows.value = answer.data\n } catch (error) {\n toast.danger(message(error))\n } finally {\n loading.value = false\n }\n}\n\nwatch(\n () => [view.value, q.value] as const,\n () => void load(),\n)\nvoid load()\n\nlet pause: ReturnType<typeof setTimeout> | undefined\n\nwatch(typed, (value) => {\n clearTimeout(pause)\n pause = setTimeout(() => {\n void router.replace({ query: { ...route.query, q: value.trim() === '' ? undefined : value } })\n }, 300)\n})\n\nonBeforeUnmount(() => clearTimeout(pause))\n\n/* `replace` and not `push`: going through twenty outlets is not twenty steps of history. */\nfunction choose(id: number | 'new'): void {\n void router.replace({ query: { ...route.query, outlet: String(id) } })\n}\n\nfunction close(): void {\n const query = { ...route.query }\n\n delete query.outlet\n void router.replace({ query })\n}\n\n/** A new outlet was saved: it is a record now, and the list has it. */\nfunction created(outlet: OutletSummary): void {\n choose(outlet.id)\n void load()\n}\n\n/**\n * The order on screen, written as a whole. The list is already in its new order\n * (`WxSortableList` reorders the model before it says so), so a failure has to put it back\n * rather than leave the screen disagreeing with the database.\n */\nasync function reorder(): Promise<void> {\n try {\n await order.move(rows.value.map((row) => row.id))\n } catch (error) {\n toast.danger(message(error, t('outlet.reorder-failed')))\n await load()\n }\n}\n\nfunction actionsFor(row: OutletRow): RowAction[] {\n if (!canManage.value) return []\n\n if (inBin.value) {\n return [\n {\n key: 'restore',\n icon: 'refresh',\n label: t('outlet.restore'),\n run: () => void restore(row),\n },\n ]\n }\n\n return [\n {\n key: 'delete',\n icon: 'trash',\n label: t('outlet.delete'),\n danger: true,\n run: () => void remove(row),\n },\n ]\n}\n\nasync function remove(row: OutletRow): Promise<void> {\n const agreed = await confirm({\n title: t('outlet.delete-title', { name: row.title }),\n message: t('outlet.delete-text'),\n confirmText: t('outlet.delete'),\n cancelText: t('outlet.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.remove(row.id)\n toast.success(t('outlet.deleted'))\n removed(row.id)\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\n/** Gone from the list; and if it was the open one, the form goes with it. */\nfunction removed(id: number): void {\n if (selected.value === id) close()\n\n void load()\n}\n\nasync function restore(row: OutletRow): Promise<void> {\n try {\n await api.restore(row.id)\n toast.success(t('outlet.restored'))\n } catch (error) {\n toast.danger(message(error))\n } finally {\n await load()\n }\n}\n\n/* Published and yet shown in no language: the one state the list has to point out. */\nfunction unseen(row: OutletRow): boolean {\n return row.published && row.locales.length === 0\n}\n</script>\n\n<template>\n <wx-list-screen v-model:view=\"view\" :title=\"title\" :views=\"views\" :card=\"false\">\n <wx-card class=\"wx-press\" padding=\"none\">\n <wx-list-detail\n v-model:open=\"open\"\n class=\"wx-press__panes\"\n :list-width=\"360\"\n :detail-min=\"520\"\n :detail-label=\"t('module.press')\"\n >\n <template #list>\n <div class=\"wx-press__bar\">\n <wx-input\n v-model=\"typed\"\n class=\"wx-press__search\"\n size=\"sm\"\n clearable\n :placeholder=\"t('outlet.search')\"\n :aria-label=\"t('outlet.search')\"\n >\n <template #prefix><wx-icon name=\"search\" size=\"sm\" /></template>\n </wx-input>\n </div>\n\n <!--\n A line of the list rather than a button in the head: the new outlet is written where\n it will stand, beside the ones it joins — and the form opens the way any other does.\n -->\n <button\n v-if=\"canManage && !inBin\"\n type=\"button\"\n class=\"wx-press__new\"\n :class=\"{ 'is-current': selected === 'new' }\"\n @click=\"choose('new')\"\n >\n <wx-icon name=\"plus\" size=\"sm\" />\n {{ t('outlet.new') }}\n </button>\n\n <wx-skeleton v-if=\"loading && rows.length === 0\" class=\"wx-press__loading\" :rows=\"5\" />\n\n <wx-empty\n v-else-if=\"rows.length === 0\"\n :title=\"emptyText\"\n :description=\"!inBin && q === '' ? t('outlet.empty-help') : undefined\"\n />\n\n <template v-else>\n <!-- A grip and not the whole row: the row opens an outlet, and a row that both opens\n and drags is one where one of the two happens by accident. -->\n <wx-sortable-list\n v-model=\"rows\"\n class=\"wx-press__list\"\n :class=\"{ 'is-loading': loading }\"\n plain\n item-key=\"id\"\n :handle=\"sortable ? 'grip' : 'row'\"\n :item-label=\"(item: OutletRow) => item.title\"\n :disabled=\"!sortable\"\n @move=\"reorder\"\n >\n <template #default=\"{ item }\">\n <component\n :is=\"inBin ? 'div' : 'button'\"\n :type=\"inBin ? undefined : 'button'\"\n class=\"wx-outlet-row\"\n :class=\"{ 'is-current': selected === item.id }\"\n @click=\"inBin ? undefined : choose(item.id)\"\n >\n <!-- A logo is wide as often as not: in a square it shrinks to a sliver, so it gets a\n box of its own shape, and only an outlet without one gets initials. -->\n <span v-if=\"item.logo?.thumb\" class=\"wx-outlet-row__logo\">\n <img :src=\"item.logo.thumb\" alt=\"\" />\n </span>\n <wx-avatar\n v-else\n class=\"wx-outlet-row__initials\"\n shape=\"square\"\n :name=\"item.title\"\n size=\"md\"\n />\n\n <span class=\"wx-outlet-row__body\">\n <span class=\"wx-outlet-row__head\">\n <span class=\"wx-outlet-row__title\">{{ item.title }}</span>\n <wx-tooltip v-if=\"item.featured\" :content=\"t('outlet.featured')\">\n <wx-icon\n class=\"wx-outlet-row__featured\"\n name=\"star\"\n size=\"sm\"\n :aria-label=\"t('outlet.featured')\"\n />\n </wx-tooltip>\n </span>\n\n <span class=\"wx-outlet-row__facts\">\n <wx-badge v-if=\"!item.published\" dot size=\"sm\">\n {{ t('outlet.not-published') }}\n </wx-badge>\n <wx-tooltip v-else-if=\"unseen(item)\" :content=\"t('outlet.visible-nowhere')\">\n <wx-badge type=\"warning\" dot size=\"sm\">\n {{ t('outlet.not-published') }}\n </wx-badge>\n </wx-tooltip>\n\n <span class=\"wx-outlet-row__count\">\n {{\n item.articles_count > 0\n ? t('outlet.articles-count', { count: item.articles_count })\n : t('outlet.no-articles')\n }}\n </span>\n\n <span\n v-if=\"item.locales.length > 0\"\n class=\"wx-outlet-row__locales\"\n :aria-label=\"t('outlet.seen-in', { locales: item.locales.join(', ') })\"\n >\n {{ item.locales.join(' · ') }}\n </span>\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, or why there is nothing to drag. -->\n <wx-alert\n v-if=\"canManage && !inBin\"\n class=\"wx-press__note\"\n type=\"info\"\n :description=\"order.hint.value\"\n />\n </template>\n </template>\n\n <template #detail=\"{ inline, back }\">\n <outlet-pane\n v-if=\"selected !== null\"\n :id=\"selected === 'new' ? null : selected\"\n :key=\"String(selected)\"\n :inline=\"inline\"\n @back=\"back\"\n @saved=\"load\"\n @created=\"created\"\n @removed=\"removed\"\n />\n </template>\n\n <template #empty>\n <wx-empty\n :title=\"t('outlet.choose')\"\n :description=\"canManage ? t('outlet.choose-help') : undefined\"\n />\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. The card's\n * corners are the screen's, and the panes paint their own background to the edge: `clip`, not\n * `hidden`, which would make this a scroll container and pin everything sticky inside to a box\n * that never moves (CLAUDE.md §4).\n */\n.wx-press > .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-press__bar {\n display: flex;\n gap: var(--wx-space-8);\n padding: var(--wx-space-12) var(--wx-space-12) var(--wx-space-8);\n}\n\n.wx-press__search {\n flex: 1 1 180px;\n min-width: 0;\n}\n\n.wx-press__new {\n display: flex;\n align-items: center;\n gap: var(--wx-space-8);\n width: calc(100% - 2 * var(--wx-space-8));\n margin: 0 var(--wx-space-8) var(--wx-space-4);\n padding: var(--wx-space-8);\n border: 1px dashed var(--wx-border-default);\n border-radius: var(--wx-radius-sm);\n background: none;\n font: inherit;\n color: var(--wx-color-primary);\n text-align: start;\n cursor: pointer;\n}\n\n.wx-press__new:hover,\n.wx-press__new.is-current {\n background: var(--wx-bg-subtle);\n}\n\n.wx-press__loading {\n padding: var(--wx-space-12);\n}\n\n.wx-press__list {\n padding-inline: var(--wx-space-8);\n}\n\n.wx-press__list.is-loading {\n opacity: 0.6;\n}\n\n.wx-press__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-press__list :deep(.wx-sortable-list__row:hover),\n.wx-press__list :deep(.wx-sortable-list__row:has(.is-current)) {\n background: var(--wx-bg-subtle);\n}\n\n.wx-press__note {\n margin: var(--wx-space-8);\n}\n\n.wx-outlet-row {\n display: flex;\n align-items: flex-start;\n gap: var(--wx-space-10);\n /* The row is the target: a short name should not leave half the line dead — and a button,\n unlike a link, is only as wide as what is in it until it is told otherwise. */\n flex: 1 1 auto;\n width: 100%;\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}\n\nbutton.wx-outlet-row {\n cursor: pointer;\n}\n\n.wx-outlet-row__initials {\n flex: none;\n}\n\n/* Twice as wide as the initials beside it, and the logo drawn whole inside: cropped, it loses\n its letters. */\n.wx-outlet-row__logo {\n display: flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 64px;\n height: 32px;\n overflow: hidden;\n border-radius: var(--wx-radius-sm);\n background: var(--wx-bg-subtle);\n}\n\n.wx-outlet-row__logo img {\n display: block;\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n}\n\n.wx-outlet-row__body {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n gap: var(--wx-space-2);\n min-width: 0;\n}\n\n.wx-outlet-row__head {\n display: flex;\n align-items: center;\n gap: var(--wx-space-6);\n min-width: 0;\n}\n\n.wx-outlet-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\n.wx-outlet-row.is-current .wx-outlet-row__title {\n color: var(--wx-color-primary);\n}\n\n.wx-outlet-row__featured {\n flex: none;\n color: var(--wx-color-warning);\n}\n\n.wx-outlet-row__facts {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--wx-space-4) var(--wx-space-8);\n min-width: 0;\n color: var(--wx-text-muted);\n font-size: var(--wx-font-size-sm);\n}\n\n.wx-outlet-row__locales {\n font-family: var(--wx-font-family-mono);\n font-size: var(--wx-font-size-xs);\n text-transform: uppercase;\n}\n</style>\n","import type { AdminModule } from '@webx-ui/module-admin'\nimport PressPage from './PressPage.vue'\n\nexport interface PressOptions {\n /** Where the outlets live inside the panel. */\n path?: string\n}\n\n/**\n * The press as a section of the panel (§4.9): one module and one entry, without a group of its\n * own — the reviews have one because they have two entries, and a group around a single item is\n * a heading over itself. A section whose server half is not installed never appears — the entry\n * is built from the manifest.\n */\nexport function press(options: PressOptions = {}): AdminModule {\n const path = options.path ?? '/press'\n\n return {\n id: 'press',\n path,\n // One route: the open outlet is in the address (`?outlet=`), beside the list it was opened\n // from, so a link to it is a link to both.\n routes: [{ path, name: 'webx.press', component: PressPage, props: { base: path } }],\n }\n}\n"],"names":["PRESS_API","createPressApi","admin","base","data","body","query","search","id","values","ids","reorderItems","pressMessages","seeded","usePressMessages","i18n","useAdmin","props","__props","emit","__emit","context","api","locales","useLocales","t","useTranslate","message","useErrorText","outlet","ref","prefix","snapshot","loading","saving","errors","canManage","computed","current","dirty","state","title","written","fallback","localizedValue","provideRecordAddress","pathIn","url","locale","segments","one","codes","own","take","detail","blank","load","error","toast","save","made","remove","record","confirm","leaveGuard","event","onKeydown","useBodyKeys","onMounted","onBeforeUnmount","mayLeave","onBeforeRouteUpdate","to","from","onBeforeRouteLeave","actions","list","_createElementBlock","_createBlock","_unref","WxSkeleton","_Fragment","_createVNode","WxScreenHead","WxBadge","WxScreen","$event","WxActionBar","WxSaveState","WxButton","route","useRoute","router","useRouter","rows","module","view","value","inBin","q","typed","selected","raw","open","close","views","order","useItemOrder","sortable","emptyText","answer","watch","pause","choose","created","reorder","row","actionsFor","restore","removed","unseen","WxListScreen","WxCard","WxListDetail","_createElementVNode","_hoisted_1","WxInput","WxIcon","_normalizeClass","_createTextVNode","WxEmpty","WxSortableList","item","_withCtx","_openBlock","_resolveDynamicComponent","_hoisted_2","WxAvatar","_hoisted_4","_hoisted_5","_hoisted_6","_toDisplayString","WxTooltip","_hoisted_7","_hoisted_8","_hoisted_9","WxRowMenu","WxAlert","inline","back","OutletPane","press","options","path","PressPage"],"mappings":"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"}
package/dist/style.css CHANGED
@@ -1 +1 @@
1
- .wx-outlet[data-v-396f08f3]{display:flex;flex-direction:column;gap:var(--wx-gap, var(--wx-space-16));min-width:0;padding:var(--wx-gap, var(--wx-space-16));container-type:inline-size}.wx-outlet__ghost[data-v-396f08f3]{max-width:480px}.wx-press>.wx-card__body{display:flex;flex-direction:column;overflow:clip;border-radius:inherit}.wx-press__bar[data-v-7ef16f83]{display:flex;gap:var(--wx-space-8);padding:var(--wx-space-12) var(--wx-space-12) var(--wx-space-8)}.wx-press__search[data-v-7ef16f83]{flex:1 1 180px;min-width:0}.wx-press__new[data-v-7ef16f83]{display:flex;align-items:center;gap:var(--wx-space-8);width:calc(100% - 2 * var(--wx-space-8));margin:0 var(--wx-space-8) var(--wx-space-4);padding:var(--wx-space-8);border:1px dashed var(--wx-border-default);border-radius:var(--wx-radius-sm);background:none;font:inherit;color:var(--wx-color-primary);text-align:start;cursor:pointer}.wx-press__new[data-v-7ef16f83]:hover,.wx-press__new.is-current[data-v-7ef16f83]{background:var(--wx-bg-subtle)}.wx-press__loading[data-v-7ef16f83]{padding:var(--wx-space-12)}.wx-press__list[data-v-7ef16f83]{padding-inline:var(--wx-space-8)}.wx-press__list.is-loading[data-v-7ef16f83]{opacity:.6}.wx-press__list[data-v-7ef16f83] .wx-sortable-list__row{gap:var(--wx-space-4);padding-inline:var(--wx-space-4);border-radius:var(--wx-radius-sm)}.wx-press__list[data-v-7ef16f83] .wx-sortable-list__row:hover,.wx-press__list[data-v-7ef16f83] .wx-sortable-list__row:has(.is-current){background:var(--wx-bg-subtle)}.wx-press__note[data-v-7ef16f83]{margin:var(--wx-space-8)}.wx-outlet-row[data-v-7ef16f83]{display:flex;align-items:flex-start;gap:var(--wx-space-10);flex:1 1 auto;width:100%;min-width:0;padding-block:var(--wx-space-8);padding-inline:0;border:0;background:none;font:inherit;color:inherit;text-align:start}button.wx-outlet-row[data-v-7ef16f83]{cursor:pointer}.wx-outlet-row__initials[data-v-7ef16f83]{flex:none}.wx-outlet-row__logo[data-v-7ef16f83]{display:flex;flex:none;align-items:center;justify-content:center;width:64px;height:32px;overflow:hidden;border-radius:var(--wx-radius-sm);background:var(--wx-bg-subtle)}.wx-outlet-row__logo img[data-v-7ef16f83]{display:block;max-width:100%;max-height:100%;object-fit:contain}.wx-outlet-row__body[data-v-7ef16f83]{display:flex;flex:1 1 auto;flex-direction:column;gap:var(--wx-space-2);min-width:0}.wx-outlet-row__head[data-v-7ef16f83]{display:flex;align-items:center;gap:var(--wx-space-6);min-width:0}.wx-outlet-row__title[data-v-7ef16f83]{min-width:0;overflow:hidden;font-weight:var(--wx-font-weight-medium);text-overflow:ellipsis;white-space:nowrap}.wx-outlet-row.is-current .wx-outlet-row__title[data-v-7ef16f83]{color:var(--wx-color-primary)}.wx-outlet-row__featured[data-v-7ef16f83]{flex:none;color:var(--wx-color-warning)}.wx-outlet-row__facts[data-v-7ef16f83]{display:flex;flex-wrap:wrap;align-items:center;gap:var(--wx-space-4) var(--wx-space-8);min-width:0;color:var(--wx-text-muted);font-size:var(--wx-font-size-sm)}.wx-outlet-row__locales[data-v-7ef16f83]{font-family:var(--wx-font-family-mono);font-size:var(--wx-font-size-xs);text-transform:uppercase}
1
+ .wx-outlet[data-v-b6ea9c8f]{display:flex;flex-direction:column;gap:var(--wx-gap, var(--wx-space-16));min-width:0;padding:var(--wx-gap, var(--wx-space-16));container-type:inline-size}.wx-outlet__ghost[data-v-b6ea9c8f]{max-width:480px}.wx-press>.wx-card__body{display:flex;flex-direction:column;overflow:clip;border-radius:inherit}.wx-press__bar[data-v-7ef16f83]{display:flex;gap:var(--wx-space-8);padding:var(--wx-space-12) var(--wx-space-12) var(--wx-space-8)}.wx-press__search[data-v-7ef16f83]{flex:1 1 180px;min-width:0}.wx-press__new[data-v-7ef16f83]{display:flex;align-items:center;gap:var(--wx-space-8);width:calc(100% - 2 * var(--wx-space-8));margin:0 var(--wx-space-8) var(--wx-space-4);padding:var(--wx-space-8);border:1px dashed var(--wx-border-default);border-radius:var(--wx-radius-sm);background:none;font:inherit;color:var(--wx-color-primary);text-align:start;cursor:pointer}.wx-press__new[data-v-7ef16f83]:hover,.wx-press__new.is-current[data-v-7ef16f83]{background:var(--wx-bg-subtle)}.wx-press__loading[data-v-7ef16f83]{padding:var(--wx-space-12)}.wx-press__list[data-v-7ef16f83]{padding-inline:var(--wx-space-8)}.wx-press__list.is-loading[data-v-7ef16f83]{opacity:.6}.wx-press__list[data-v-7ef16f83] .wx-sortable-list__row{gap:var(--wx-space-4);padding-inline:var(--wx-space-4);border-radius:var(--wx-radius-sm)}.wx-press__list[data-v-7ef16f83] .wx-sortable-list__row:hover,.wx-press__list[data-v-7ef16f83] .wx-sortable-list__row:has(.is-current){background:var(--wx-bg-subtle)}.wx-press__note[data-v-7ef16f83]{margin:var(--wx-space-8)}.wx-outlet-row[data-v-7ef16f83]{display:flex;align-items:flex-start;gap:var(--wx-space-10);flex:1 1 auto;width:100%;min-width:0;padding-block:var(--wx-space-8);padding-inline:0;border:0;background:none;font:inherit;color:inherit;text-align:start}button.wx-outlet-row[data-v-7ef16f83]{cursor:pointer}.wx-outlet-row__initials[data-v-7ef16f83]{flex:none}.wx-outlet-row__logo[data-v-7ef16f83]{display:flex;flex:none;align-items:center;justify-content:center;width:64px;height:32px;overflow:hidden;border-radius:var(--wx-radius-sm);background:var(--wx-bg-subtle)}.wx-outlet-row__logo img[data-v-7ef16f83]{display:block;max-width:100%;max-height:100%;object-fit:contain}.wx-outlet-row__body[data-v-7ef16f83]{display:flex;flex:1 1 auto;flex-direction:column;gap:var(--wx-space-2);min-width:0}.wx-outlet-row__head[data-v-7ef16f83]{display:flex;align-items:center;gap:var(--wx-space-6);min-width:0}.wx-outlet-row__title[data-v-7ef16f83]{min-width:0;overflow:hidden;font-weight:var(--wx-font-weight-medium);text-overflow:ellipsis;white-space:nowrap}.wx-outlet-row.is-current .wx-outlet-row__title[data-v-7ef16f83]{color:var(--wx-color-primary)}.wx-outlet-row__featured[data-v-7ef16f83]{flex:none;color:var(--wx-color-warning)}.wx-outlet-row__facts[data-v-7ef16f83]{display:flex;flex-wrap:wrap;align-items:center;gap:var(--wx-space-4) var(--wx-space-8);min-width:0;color:var(--wx-text-muted);font-size:var(--wx-font-size-sm)}.wx-outlet-row__locales[data-v-7ef16f83]{font-family:var(--wx-font-family-mono);font-size:var(--wx-font-size-xs);text-transform:uppercase}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webx-ui/module-press",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Press for the WebX UI admin panel: the outlets that wrote about a site and their articles — a link or a PDF — ordered by hand, shown on the site by blocks.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -44,9 +44,9 @@
44
44
  "vue-router": "^4.5.0"
45
45
  },
46
46
  "dependencies": {
47
- "@webx-ui/core": "^0.34.0",
48
- "@webx-ui/module-admin": "^0.18.1",
49
- "@webx-ui/schema": "^0.6.2"
47
+ "@webx-ui/core": "^0.34.1",
48
+ "@webx-ui/module-admin": "^0.18.2",
49
+ "@webx-ui/schema": "^0.6.4"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@types/node": "^24.10.1",