@dskripchenko/laravel-admin 1.43.0 → 1.44.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.
Files changed (38) hide show
  1. package/dist/.vite/manifest.json +2 -2
  2. package/dist/assets/app-CwIxrlX6.css +1 -0
  3. package/dist/assets/app-CxQTEfcf.js +44 -0
  4. package/dist/components/dashboard/GaugeWidget.vue.d.ts +1 -1
  5. package/dist/components/dashboard/StatWidget.vue.d.ts +9 -0
  6. package/dist/components/dashboard/chartGeometry.d.ts +2 -2
  7. package/dist/components/fields/repeaterOrder.test.d.ts +1 -0
  8. package/dist/components/hardcodedText.test.d.ts +1 -0
  9. package/dist/components/layouts/ResourceIndexLayout.vue.d.ts +8 -0
  10. package/dist/components/profile/ProfilePage.vue.d.ts +4 -4
  11. package/dist/components/resource/InlineEditCell.vue.d.ts +1 -1
  12. package/dist/components/resource/ResourceIndexPage.contract.test.d.ts +1 -0
  13. package/dist/components/resource/ResourceIndexPage.vue.d.ts +2 -2
  14. package/dist/components/resource/ResourceIndexRowClick.test.d.ts +1 -0
  15. package/dist/components/resource/reorder.d.ts +33 -0
  16. package/dist/components/resource/reorder.test.d.ts +1 -0
  17. package/dist/i18n-D77Oo6mM.js +89 -0
  18. package/dist/i18n-D77Oo6mM.js.map +1 -0
  19. package/dist/i18n-D98X6Xot.cjs +2 -0
  20. package/dist/i18n-D98X6Xot.cjs.map +1 -0
  21. package/dist/index.cjs +23 -23
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.mjs +15357 -15057
  24. package/dist/index.mjs.map +1 -1
  25. package/dist/source-hash.txt +1 -1
  26. package/dist/stores/i18n.d.ts +17 -0
  27. package/dist/stores/resourceIndex.d.ts +4 -1
  28. package/dist/style.css +1 -1
  29. package/dist/styles/tokens.guard.test.d.ts +1 -0
  30. package/dist/tinymce.cjs +1 -1
  31. package/dist/tinymce.mjs +1 -1
  32. package/package.json +3 -3
  33. package/dist/assets/app-By8uSJii.css +0 -1
  34. package/dist/assets/app-CIFUCoeu.js +0 -44
  35. package/dist/i18n-BxCQCcGB.cjs +0 -2
  36. package/dist/i18n-BxCQCcGB.cjs.map +0 -1
  37. package/dist/i18n-Vgxu-A6P.js +0 -66
  38. package/dist/i18n-Vgxu-A6P.js.map +0 -1
@@ -34,11 +34,11 @@ declare const _default: import("vue").DefineComponent<Props, {}, {}, {}, {}, imp
34
34
  color: string;
35
35
  max: number;
36
36
  min: number;
37
+ unit: string;
37
38
  suffix: string;
38
39
  precision: number | null;
39
40
  tone: SemanticTone;
40
41
  ranges: GaugeRange[];
41
42
  thresholds: GaugeRange[];
42
- unit: string;
43
43
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
44
44
  export default _default;
@@ -3,11 +3,20 @@ interface StatChange {
3
3
  delta?: number;
4
4
  direction?: 'up' | 'down' | 'flat';
5
5
  }
6
+ interface StatFormat {
7
+ style?: 'currency';
8
+ currency?: string;
9
+ decimals?: number;
10
+ }
6
11
  interface StatItem {
7
12
  label?: string;
8
13
  value?: number | string;
9
14
  prefix?: string;
10
15
  suffix?: string;
16
+ /** StatsOverviewWidget::money(). */
17
+ format?: StatFormat | null;
18
+ /** StatsOverviewWidget::precision(). */
19
+ precision?: number | null;
11
20
  change?: StatChange | null;
12
21
  color?: string | null;
13
22
  icon?: string | null;
@@ -50,9 +50,9 @@ export interface NiceScale {
50
50
  * always inside, so bars and areas grow from a real baseline.
51
51
  */
52
52
  export declare function niceScale(min: number, max: number, count?: number): NiceScale;
53
- /** An axis tick: compact (1.2K, 3M). */
53
+ /** An axis tick: compact (1.2K, 3M), in the panel's locale. */
54
54
  export declare function formatTick(v: number): string;
55
- /** A value in a tooltip or the data table: full, with grouping. */
55
+ /** A value in a tooltip or the data table: full, with grouping, in the panel's locale. */
56
56
  export declare function formatValue(v: number | null): string;
57
57
  /** A rough text width in px for the axis font — enough to keep labels apart. */
58
58
  export declare function estimateTextWidth(text: string, fontPx?: number): number;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ interface Props {
2
+ resource: string;
3
+ title?: string | null;
4
+ }
5
+ declare const _default: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {
6
+ title: string | null;
7
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
8
+ export default _default;
@@ -1,18 +1,18 @@
1
1
  interface Props {
2
- /** The page's title; "Profile" by default. */
2
+ /** The page's title; «Профиль», translated, by default. */
3
3
  title?: string;
4
4
  /** The subtitle; taken from the handoff by default. */
5
5
  subtitle?: string;
6
6
  /** Which section is active. */
7
7
  section?: 'general' | 'security' | 'tokens' | 'sessions' | string;
8
8
  }
9
- declare var __VLS_53: {}, __VLS_64: string, __VLS_65: {
9
+ declare var __VLS_61: {}, __VLS_72: string, __VLS_73: {
10
10
  section: string;
11
11
  };
12
12
  type __VLS_Slots = {} & {
13
- [K in NonNullable<typeof __VLS_64>]?: (props: typeof __VLS_65) => any;
13
+ [K in NonNullable<typeof __VLS_72>]?: (props: typeof __VLS_73) => any;
14
14
  } & {
15
- 'enable-2fa'?: (props: typeof __VLS_53) => any;
15
+ 'enable-2fa'?: (props: typeof __VLS_61) => any;
16
16
  };
17
17
  declare const __VLS_component: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
18
18
  save: (payload: {
@@ -4,7 +4,7 @@ interface Props {
4
4
  rowId: string | number;
5
5
  column: string;
6
6
  value: unknown;
7
- /** With false a double click does nothing: the cell is read-only. */
7
+ /** With false a click does nothing here and reaches the row: the cell is read-only. */
8
8
  editable?: boolean;
9
9
  /** The kind of editing control. */
10
10
  inputType?: InlineInputType;
@@ -13,11 +13,11 @@ interface Props {
13
13
  declare var __VLS_5: {}, __VLS_78: {}, __VLS_147: {
14
14
  filters: Record<string, unknown>;
15
15
  setFilter: (key: string, value: unknown) => Promise<void>;
16
- }, __VLS_228: `cell-${string}`, __VLS_229: {
16
+ }, __VLS_252: `cell-${string}`, __VLS_253: {
17
17
  row: Record<string, unknown> | undefined;
18
18
  };
19
19
  type __VLS_Slots = {} & {
20
- [K in NonNullable<typeof __VLS_228>]?: (props: typeof __VLS_229) => any;
20
+ [K in NonNullable<typeof __VLS_252>]?: (props: typeof __VLS_253) => any;
21
21
  } & {
22
22
  actions?: (props: typeof __VLS_5) => any;
23
23
  } & {
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The drag-and-drop reordering of a resource list — the request the list page
3
+ * sends to `POST /{slug}/reorder` and the rules around it, kept apart from
4
+ * the page so the contract with ResourceController::reorder() is tested on
5
+ * its own.
6
+ *
7
+ * The body is `{ids, offset}`: the visible rows in their new order and the
8
+ * index of the page's first row. The server hands those rows the positions
9
+ * they already hold, smallest first, and numbers them from `offset` only
10
+ * when those positions are unusable — so a page of a paginated or filtered
11
+ * list reorders within its own slots.
12
+ */
13
+ export interface ReorderPayload {
14
+ ids: Array<string | number>;
15
+ offset: number;
16
+ }
17
+ export interface ReorderResponse {
18
+ count?: number;
19
+ /** The new positions by primary key. */
20
+ positions?: Record<string, number>;
21
+ }
22
+ export declare function buildReorderPayload(ids: Array<string | number>, page: number, perPage: number): ReorderPayload;
23
+ /**
24
+ * Whether dragging may reorder the rows: only in the manual order — no sort,
25
+ * or the reorder column ascending. Under any other sort the visible sequence
26
+ * is not the stored one, and a drop would scramble it.
27
+ */
28
+ export declare function canDragReorder(reorderable: boolean, reorderColumn: string | null | undefined, sortKey: string | null, sortDirection: 'asc' | 'desc' | null): boolean;
29
+ /**
30
+ * The rows with the positions the server answered, written into the reorder
31
+ * column where the rows carry it. Rows without a new position stay as they are.
32
+ */
33
+ export declare function applyPositions(rows: Array<Record<string, unknown>>, positions: Record<string, number> | undefined, column: string | null | undefined, rowId: (row: Record<string, unknown>) => string | number): Array<Record<string, unknown>>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,89 @@
1
+ import { defineStore as p } from "pinia";
2
+ import { ref as l, computed as b } from "vue";
3
+ function j(t) {
4
+ try {
5
+ return s().tr(t);
6
+ } catch {
7
+ return t;
8
+ }
9
+ }
10
+ function v() {
11
+ if (typeof document < "u" && document.documentElement.lang)
12
+ return document.documentElement.lang;
13
+ try {
14
+ const t = s().locale;
15
+ if (t) return t;
16
+ } catch {
17
+ }
18
+ return "ru";
19
+ }
20
+ function y(t) {
21
+ const e = t.replace(/_/g, "-");
22
+ try {
23
+ return Intl.NumberFormat.supportedLocalesOf([e]).length > 0 ? e : "en";
24
+ } catch {
25
+ return "en";
26
+ }
27
+ }
28
+ function h() {
29
+ return y(v());
30
+ }
31
+ const f = /* @__PURE__ */ new Map();
32
+ function S(t = {}) {
33
+ const e = h(), r = `${e}|${JSON.stringify(t)}`;
34
+ let o = f.get(r);
35
+ return o || (o = new Intl.NumberFormat(e, t), f.set(r, o)), o;
36
+ }
37
+ function E(t, e = {}) {
38
+ return S(e).format(t);
39
+ }
40
+ const s = p("admin-i18n", () => {
41
+ const t = l({}), e = l("ru");
42
+ function r(n) {
43
+ const c = n.translations;
44
+ c && typeof c == "object" && (t.value = { ...c }), e.value = n.locale ?? "ru";
45
+ }
46
+ function o(n) {
47
+ t.value = { ...n };
48
+ }
49
+ function u(n, c = {}) {
50
+ let a = t.value[n] ?? n;
51
+ for (const [g, d] of Object.entries(c))
52
+ a = a.replace(new RegExp(`:${g}`, "g"), String(d));
53
+ return a;
54
+ }
55
+ const i = (n) => n in t.value;
56
+ function m(n) {
57
+ return t.value[n] ?? n;
58
+ }
59
+ return {
60
+ messages: t,
61
+ locale: e,
62
+ hydrate: r,
63
+ setMessages: o,
64
+ t: u,
65
+ tr: m,
66
+ has: i,
67
+ keys: b(() => Object.keys(t.value))
68
+ };
69
+ });
70
+ function F(t, e) {
71
+ try {
72
+ return s().t(t, e ?? {});
73
+ } catch {
74
+ let r = t;
75
+ for (const [o, u] of Object.entries(e ?? {}))
76
+ r = r.replace(new RegExp(`:${o}`, "g"), String(u));
77
+ return r;
78
+ }
79
+ }
80
+ export {
81
+ F as a,
82
+ E as b,
83
+ v as c,
84
+ h as f,
85
+ y as i,
86
+ j as t,
87
+ s as u
88
+ };
89
+ //# sourceMappingURL=i18n-D77Oo6mM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i18n-D77Oo6mM.js","sources":["../resources/ts/stores/i18n.ts"],"sourcesContent":["/**\n * The i18n store — a plain message bag plus the t() helper.\n *\n * The backend puts the bag into `bootstrap.translations`, a\n * Record<key, string>. When the locale changes (`/system/setLocale`) either\n * the bootstrap is raised again or the helper calls `loadLocale(locale)`,\n * which POSTs for the messages.\n *\n * The form:\n * t('admin.dashboard.add_widget') // 'Add widget'\n * t('admin.records.count', { n: 42 }) // 'Records: 42', interpolating :n\n *\n * The fallback: a key that is not found is returned as it is, which makes\n * missing translations easy to spot during development.\n */\nimport { defineStore } from 'pinia'\nimport { computed, ref } from 'vue'\nimport type { AdminBootstrap } from '../types/bootstrap'\n\n/**\n * A standalone wrapper around store.tr() for the components: it does not fall\n * over without an active Pinia (unit tests, SSR edges) and returns the source\n * string instead.\n */\nexport function trSafe(text: string): string {\n try {\n return useI18nStore().tr(text)\n } catch {\n return text\n }\n}\n\n/**\n * The panel's current language, for formatting — month names, numbers: the\n * document's lang, else the bootstrap locale, else Russian, the source\n * language.\n */\nexport function currentLocale(): string {\n // The locale store keeps <html lang> in step with a switch made in place.\n if (typeof document !== 'undefined' && document.documentElement.lang) {\n return document.documentElement.lang\n }\n try {\n const locale = useI18nStore().locale\n if (locale) return locale\n } catch {\n // no active Pinia\n }\n return 'ru'\n}\n\n/**\n * A panel locale as a tag Intl accepts: Laravel's `pt_BR` becomes `pt-BR`,\n * and a tag Intl does not know falls back to English.\n */\nexport function intlLocale(locale: string): string {\n const tag = locale.replace(/_/g, '-')\n try {\n return Intl.NumberFormat.supportedLocalesOf([tag]).length > 0 ? tag : 'en'\n } catch {\n return 'en'\n }\n}\n\n/** The locale given to Intl: the panel's own; see intlLocale(). */\nexport function formatLocale(): string {\n return intlLocale(currentLocale())\n}\n\nconst numberFormats = new Map<string, Intl.NumberFormat>()\n\n/**\n * A number formatter in the panel's locale — decimal and group separators,\n * currencies — cached per locale and options. Never the browser's locale:\n * an English panel in a Russian browser shows English numbers.\n */\nexport function numberFormat(options: Intl.NumberFormatOptions = {}): Intl.NumberFormat {\n const locale = formatLocale()\n const key = `${locale}|${JSON.stringify(options)}`\n let format = numberFormats.get(key)\n if (!format) {\n format = new Intl.NumberFormat(locale, options)\n numberFormats.set(key, format)\n }\n return format\n}\n\n/** Formats a number in the panel's locale; see numberFormat(). */\nexport function formatNumber(value: number, options: Intl.NumberFormatOptions = {}): string {\n return numberFormat(options).format(value)\n}\n\n/** Formats a date in the panel's locale. */\nexport function formatDate(value: Date | string, options: Intl.DateTimeFormatOptions = {}): string {\n const date = typeof value === 'string' ? new Date(value) : value\n if (Number.isNaN(date.getTime())) return ''\n return date.toLocaleString(formatLocale(), options)\n}\n\nexport const useI18nStore = defineStore('admin-i18n', () => {\n const messages = ref<Record<string, string>>({})\n const locale = ref<string>('ru')\n\n function hydrate(bootstrap: AdminBootstrap): void {\n // The backend may put the translations into the bootstrap.\n const t = (bootstrap as unknown as { translations?: Record<string, string> }).translations\n if (t && typeof t === 'object') {\n messages.value = { ...t }\n }\n locale.value = bootstrap.locale ?? 'ru'\n }\n\n function setMessages(next: Record<string, string>): void {\n messages.value = { ...next }\n }\n\n /**\n * Translates, supporting Laravel-style `:name` interpolation. A missing key\n * is returned as it is, which makes the gap visible.\n */\n function t(key: string, replace: Record<string, string | number> = {}): string {\n let str = messages.value[key] ?? key\n for (const [k, v] of Object.entries(replace)) {\n str = str.replace(new RegExp(`:${k}`, 'g'), String(v))\n }\n return str\n }\n\n const has = (key: string): boolean => key in messages.value\n\n /**\n * Translates by a string key, JSON-style, where the key is the source string\n * in the development language. The backend mixes the current locale's JSON\n * translations — the host's lang/{locale}.json — into the bag. Without a\n * translation the string comes back untouched, with no interpolation.\n */\n function tr(text: string): string {\n return messages.value[text] ?? text\n }\n\n return {\n messages,\n locale,\n hydrate,\n setMessages,\n t,\n tr,\n has,\n keys: computed(() => Object.keys(messages.value)),\n }\n})\n\n/**\n * A convenience: the global t() for non-Vue contexts — utilities, services. In\n * a Vue component prefer `const { t } = useI18nStore()`.\n */\nexport function tRaw(key: string, replace?: Record<string, string | number>): string {\n try {\n return useI18nStore().t(key, replace ?? {})\n } catch {\n // Without an active Pinia — unit tests, a render before the\n // initialization — we return the source string with the values\n // substituted. This used to throw and take the whole component down: an\n // untranslated string beats an empty page.\n let str = key\n for (const [k, v] of Object.entries(replace ?? {})) {\n str = str.replace(new RegExp(`:${k}`, 'g'), String(v))\n }\n\n return str\n }\n}\n"],"names":["trSafe","text","useI18nStore","currentLocale","locale","intlLocale","tag","formatLocale","numberFormats","numberFormat","options","key","format","formatNumber","value","defineStore","messages","ref","hydrate","bootstrap","t","setMessages","next","replace","str","k","v","has","tr","computed","tRaw"],"mappings":";;AAwBO,SAASA,EAAOC,GAAsB;AAC3C,MAAI;AACF,WAAOC,EAAA,EAAe,GAAGD,CAAI;AAAA,EAC/B,QAAQ;AACN,WAAOA;AAAA,EACT;AACF;AAOO,SAASE,IAAwB;AAEtC,MAAI,OAAO,WAAa,OAAe,SAAS,gBAAgB;AAC9D,WAAO,SAAS,gBAAgB;AAElC,MAAI;AACF,UAAMC,IAASF,IAAe;AAC9B,QAAIE,EAAQ,QAAOA;AAAA,EACrB,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAMO,SAASC,EAAWD,GAAwB;AACjD,QAAME,IAAMF,EAAO,QAAQ,MAAM,GAAG;AACpC,MAAI;AACF,WAAO,KAAK,aAAa,mBAAmB,CAACE,CAAG,CAAC,EAAE,SAAS,IAAIA,IAAM;AAAA,EACxE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAASC,IAAuB;AACrC,SAAOF,EAAWF,GAAe;AACnC;AAEA,MAAMK,wBAAoB,IAAA;AAOnB,SAASC,EAAaC,IAAoC,IAAuB;AACtF,QAAMN,IAASG,EAAA,GACTI,IAAM,GAAGP,CAAM,IAAI,KAAK,UAAUM,CAAO,CAAC;AAChD,MAAIE,IAASJ,EAAc,IAAIG,CAAG;AAClC,SAAKC,MACHA,IAAS,IAAI,KAAK,aAAaR,GAAQM,CAAO,GAC9CF,EAAc,IAAIG,GAAKC,CAAM,IAExBA;AACT;AAGO,SAASC,EAAaC,GAAeJ,IAAoC,IAAY;AAC1F,SAAOD,EAAaC,CAAO,EAAE,OAAOI,CAAK;AAC3C;AASO,MAAMZ,IAAea,EAAY,cAAc,MAAM;AAC1D,QAAMC,IAAWC,EAA4B,EAAE,GACzCb,IAASa,EAAY,IAAI;AAE/B,WAASC,EAAQC,GAAiC;AAEhD,UAAMC,IAAKD,EAAmE;AAC9E,IAAIC,KAAK,OAAOA,KAAM,aACpBJ,EAAS,QAAQ,EAAE,GAAGI,EAAAA,IAExBhB,EAAO,QAAQe,EAAU,UAAU;AAAA,EACrC;AAEA,WAASE,EAAYC,GAAoC;AACvD,IAAAN,EAAS,QAAQ,EAAE,GAAGM,EAAA;AAAA,EACxB;AAMA,WAASF,EAAET,GAAaY,IAA2C,IAAY;AAC7E,QAAIC,IAAMR,EAAS,MAAML,CAAG,KAAKA;AACjC,eAAW,CAACc,GAAGC,CAAC,KAAK,OAAO,QAAQH,CAAO;AACzC,MAAAC,IAAMA,EAAI,QAAQ,IAAI,OAAO,IAAIC,CAAC,IAAI,GAAG,GAAG,OAAOC,CAAC,CAAC;AAEvD,WAAOF;AAAA,EACT;AAEA,QAAMG,IAAM,CAAChB,MAAyBA,KAAOK,EAAS;AAQtD,WAASY,EAAG3B,GAAsB;AAChC,WAAOe,EAAS,MAAMf,CAAI,KAAKA;AAAA,EACjC;AAEA,SAAO;AAAA,IACL,UAAAe;AAAA,IACA,QAAAZ;AAAA,IACA,SAAAc;AAAA,IACA,aAAAG;AAAA,IACA,GAAAD;AAAA,IACA,IAAAQ;AAAA,IACA,KAAAD;AAAA,IACA,MAAME,EAAS,MAAM,OAAO,KAAKb,EAAS,KAAK,CAAC;AAAA,EAAA;AAEpD,CAAC;AAMM,SAASc,EAAKnB,GAAaY,GAAmD;AACnF,MAAI;AACF,WAAOrB,IAAe,EAAES,GAAKY,KAAW,CAAA,CAAE;AAAA,EAC5C,QAAQ;AAKN,QAAIC,IAAMb;AACV,eAAW,CAACc,GAAGC,CAAC,KAAK,OAAO,QAAQH,KAAW,CAAA,CAAE;AAC/C,MAAAC,IAAMA,EAAI,QAAQ,IAAI,OAAO,IAAIC,CAAC,IAAI,GAAG,GAAG,OAAOC,CAAC,CAAC;AAGvD,WAAOF;AAAA,EACT;AACF;"}
@@ -0,0 +1,2 @@
1
+ "use strict";const y=require("pinia"),s=require("vue");function S(e){try{return u().tr(e)}catch{return e}}function i(){if(typeof document<"u"&&document.documentElement.lang)return document.documentElement.lang;try{const e=u().locale;if(e)return e}catch{}return"ru"}function m(e){const t=e.replace(/_/g,"-");try{return Intl.NumberFormat.supportedLocalesOf([t]).length>0?t:"en"}catch{return"en"}}function g(){return m(i())}const f=new Map;function h(e={}){const t=g(),r=`${t}|${JSON.stringify(e)}`;let o=f.get(r);return o||(o=new Intl.NumberFormat(t,e),f.set(r,o)),o}function L(e,t={}){return h(t).format(e)}const u=y.defineStore("admin-i18n",()=>{const e=s.ref({}),t=s.ref("ru");function r(n){const c=n.translations;c&&typeof c=="object"&&(e.value={...c}),t.value=n.locale??"ru"}function o(n){e.value={...n}}function a(n,c={}){let l=e.value[n]??n;for(const[b,p]of Object.entries(c))l=l.replace(new RegExp(`:${b}`,"g"),String(p));return l}const d=n=>n in e.value;function v(n){return e.value[n]??n}return{messages:e,locale:t,hydrate:r,setMessages:o,t:a,tr:v,has:d,keys:s.computed(()=>Object.keys(e.value))}});function w(e,t){try{return u().t(e,t??{})}catch{let r=e;for(const[o,a]of Object.entries(t??{}))r=r.replace(new RegExp(`:${o}`,"g"),String(a));return r}}exports.currentLocale=i;exports.formatLocale=g;exports.formatNumber=L;exports.intlLocale=m;exports.tRaw=w;exports.trSafe=S;exports.useI18nStore=u;
2
+ //# sourceMappingURL=i18n-D98X6Xot.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i18n-D98X6Xot.cjs","sources":["../resources/ts/stores/i18n.ts"],"sourcesContent":["/**\n * The i18n store — a plain message bag plus the t() helper.\n *\n * The backend puts the bag into `bootstrap.translations`, a\n * Record<key, string>. When the locale changes (`/system/setLocale`) either\n * the bootstrap is raised again or the helper calls `loadLocale(locale)`,\n * which POSTs for the messages.\n *\n * The form:\n * t('admin.dashboard.add_widget') // 'Add widget'\n * t('admin.records.count', { n: 42 }) // 'Records: 42', interpolating :n\n *\n * The fallback: a key that is not found is returned as it is, which makes\n * missing translations easy to spot during development.\n */\nimport { defineStore } from 'pinia'\nimport { computed, ref } from 'vue'\nimport type { AdminBootstrap } from '../types/bootstrap'\n\n/**\n * A standalone wrapper around store.tr() for the components: it does not fall\n * over without an active Pinia (unit tests, SSR edges) and returns the source\n * string instead.\n */\nexport function trSafe(text: string): string {\n try {\n return useI18nStore().tr(text)\n } catch {\n return text\n }\n}\n\n/**\n * The panel's current language, for formatting — month names, numbers: the\n * document's lang, else the bootstrap locale, else Russian, the source\n * language.\n */\nexport function currentLocale(): string {\n // The locale store keeps <html lang> in step with a switch made in place.\n if (typeof document !== 'undefined' && document.documentElement.lang) {\n return document.documentElement.lang\n }\n try {\n const locale = useI18nStore().locale\n if (locale) return locale\n } catch {\n // no active Pinia\n }\n return 'ru'\n}\n\n/**\n * A panel locale as a tag Intl accepts: Laravel's `pt_BR` becomes `pt-BR`,\n * and a tag Intl does not know falls back to English.\n */\nexport function intlLocale(locale: string): string {\n const tag = locale.replace(/_/g, '-')\n try {\n return Intl.NumberFormat.supportedLocalesOf([tag]).length > 0 ? tag : 'en'\n } catch {\n return 'en'\n }\n}\n\n/** The locale given to Intl: the panel's own; see intlLocale(). */\nexport function formatLocale(): string {\n return intlLocale(currentLocale())\n}\n\nconst numberFormats = new Map<string, Intl.NumberFormat>()\n\n/**\n * A number formatter in the panel's locale — decimal and group separators,\n * currencies — cached per locale and options. Never the browser's locale:\n * an English panel in a Russian browser shows English numbers.\n */\nexport function numberFormat(options: Intl.NumberFormatOptions = {}): Intl.NumberFormat {\n const locale = formatLocale()\n const key = `${locale}|${JSON.stringify(options)}`\n let format = numberFormats.get(key)\n if (!format) {\n format = new Intl.NumberFormat(locale, options)\n numberFormats.set(key, format)\n }\n return format\n}\n\n/** Formats a number in the panel's locale; see numberFormat(). */\nexport function formatNumber(value: number, options: Intl.NumberFormatOptions = {}): string {\n return numberFormat(options).format(value)\n}\n\n/** Formats a date in the panel's locale. */\nexport function formatDate(value: Date | string, options: Intl.DateTimeFormatOptions = {}): string {\n const date = typeof value === 'string' ? new Date(value) : value\n if (Number.isNaN(date.getTime())) return ''\n return date.toLocaleString(formatLocale(), options)\n}\n\nexport const useI18nStore = defineStore('admin-i18n', () => {\n const messages = ref<Record<string, string>>({})\n const locale = ref<string>('ru')\n\n function hydrate(bootstrap: AdminBootstrap): void {\n // The backend may put the translations into the bootstrap.\n const t = (bootstrap as unknown as { translations?: Record<string, string> }).translations\n if (t && typeof t === 'object') {\n messages.value = { ...t }\n }\n locale.value = bootstrap.locale ?? 'ru'\n }\n\n function setMessages(next: Record<string, string>): void {\n messages.value = { ...next }\n }\n\n /**\n * Translates, supporting Laravel-style `:name` interpolation. A missing key\n * is returned as it is, which makes the gap visible.\n */\n function t(key: string, replace: Record<string, string | number> = {}): string {\n let str = messages.value[key] ?? key\n for (const [k, v] of Object.entries(replace)) {\n str = str.replace(new RegExp(`:${k}`, 'g'), String(v))\n }\n return str\n }\n\n const has = (key: string): boolean => key in messages.value\n\n /**\n * Translates by a string key, JSON-style, where the key is the source string\n * in the development language. The backend mixes the current locale's JSON\n * translations — the host's lang/{locale}.json — into the bag. Without a\n * translation the string comes back untouched, with no interpolation.\n */\n function tr(text: string): string {\n return messages.value[text] ?? text\n }\n\n return {\n messages,\n locale,\n hydrate,\n setMessages,\n t,\n tr,\n has,\n keys: computed(() => Object.keys(messages.value)),\n }\n})\n\n/**\n * A convenience: the global t() for non-Vue contexts — utilities, services. In\n * a Vue component prefer `const { t } = useI18nStore()`.\n */\nexport function tRaw(key: string, replace?: Record<string, string | number>): string {\n try {\n return useI18nStore().t(key, replace ?? {})\n } catch {\n // Without an active Pinia — unit tests, a render before the\n // initialization — we return the source string with the values\n // substituted. This used to throw and take the whole component down: an\n // untranslated string beats an empty page.\n let str = key\n for (const [k, v] of Object.entries(replace ?? {})) {\n str = str.replace(new RegExp(`:${k}`, 'g'), String(v))\n }\n\n return str\n }\n}\n"],"names":["trSafe","text","useI18nStore","currentLocale","locale","intlLocale","tag","formatLocale","numberFormats","numberFormat","options","key","format","formatNumber","value","defineStore","messages","ref","hydrate","bootstrap","t","setMessages","next","replace","str","k","v","has","tr","computed","tRaw"],"mappings":"uDAwBO,SAASA,EAAOC,EAAsB,CAC3C,GAAI,CACF,OAAOC,EAAA,EAAe,GAAGD,CAAI,CAC/B,MAAQ,CACN,OAAOA,CACT,CACF,CAOO,SAASE,GAAwB,CAEtC,GAAI,OAAO,SAAa,KAAe,SAAS,gBAAgB,KAC9D,OAAO,SAAS,gBAAgB,KAElC,GAAI,CACF,MAAMC,EAASF,IAAe,OAC9B,GAAIE,EAAQ,OAAOA,CACrB,MAAQ,CAER,CACA,MAAO,IACT,CAMO,SAASC,EAAWD,EAAwB,CACjD,MAAME,EAAMF,EAAO,QAAQ,KAAM,GAAG,EACpC,GAAI,CACF,OAAO,KAAK,aAAa,mBAAmB,CAACE,CAAG,CAAC,EAAE,OAAS,EAAIA,EAAM,IACxE,MAAQ,CACN,MAAO,IACT,CACF,CAGO,SAASC,GAAuB,CACrC,OAAOF,EAAWF,GAAe,CACnC,CAEA,MAAMK,MAAoB,IAOnB,SAASC,EAAaC,EAAoC,GAAuB,CACtF,MAAMN,EAASG,EAAA,EACTI,EAAM,GAAGP,CAAM,IAAI,KAAK,UAAUM,CAAO,CAAC,GAChD,IAAIE,EAASJ,EAAc,IAAIG,CAAG,EAClC,OAAKC,IACHA,EAAS,IAAI,KAAK,aAAaR,EAAQM,CAAO,EAC9CF,EAAc,IAAIG,EAAKC,CAAM,GAExBA,CACT,CAGO,SAASC,EAAaC,EAAeJ,EAAoC,GAAY,CAC1F,OAAOD,EAAaC,CAAO,EAAE,OAAOI,CAAK,CAC3C,CASO,MAAMZ,EAAea,EAAAA,YAAY,aAAc,IAAM,CAC1D,MAAMC,EAAWC,EAAAA,IAA4B,EAAE,EACzCb,EAASa,EAAAA,IAAY,IAAI,EAE/B,SAASC,EAAQC,EAAiC,CAEhD,MAAMC,EAAKD,EAAmE,aAC1EC,GAAK,OAAOA,GAAM,WACpBJ,EAAS,MAAQ,CAAE,GAAGI,CAAAA,GAExBhB,EAAO,MAAQe,EAAU,QAAU,IACrC,CAEA,SAASE,EAAYC,EAAoC,CACvDN,EAAS,MAAQ,CAAE,GAAGM,CAAA,CACxB,CAMA,SAASF,EAAET,EAAaY,EAA2C,GAAY,CAC7E,IAAIC,EAAMR,EAAS,MAAML,CAAG,GAAKA,EACjC,SAAW,CAACc,EAAGC,CAAC,IAAK,OAAO,QAAQH,CAAO,EACzCC,EAAMA,EAAI,QAAQ,IAAI,OAAO,IAAIC,CAAC,GAAI,GAAG,EAAG,OAAOC,CAAC,CAAC,EAEvD,OAAOF,CACT,CAEA,MAAMG,EAAOhB,GAAyBA,KAAOK,EAAS,MAQtD,SAASY,EAAG3B,EAAsB,CAChC,OAAOe,EAAS,MAAMf,CAAI,GAAKA,CACjC,CAEA,MAAO,CACL,SAAAe,EACA,OAAAZ,EACA,QAAAc,EACA,YAAAG,EACA,EAAAD,EACA,GAAAQ,EACA,IAAAD,EACA,KAAME,EAAAA,SAAS,IAAM,OAAO,KAAKb,EAAS,KAAK,CAAC,CAAA,CAEpD,CAAC,EAMM,SAASc,EAAKnB,EAAaY,EAAmD,CACnF,GAAI,CACF,OAAOrB,IAAe,EAAES,EAAKY,GAAW,CAAA,CAAE,CAC5C,MAAQ,CAKN,IAAIC,EAAMb,EACV,SAAW,CAACc,EAAGC,CAAC,IAAK,OAAO,QAAQH,GAAW,CAAA,CAAE,EAC/CC,EAAMA,EAAI,QAAQ,IAAI,OAAO,IAAIC,CAAC,GAAI,GAAG,EAAG,OAAOC,CAAC,CAAC,EAGvD,OAAOF,CACT,CACF"}