@revenexx/studio-shared 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/dist/module.d.mts +5 -3
  2. package/dist/module.json +2 -2
  3. package/dist/module.mjs +7 -3
  4. package/dist/runtime/components/Dashboard/DashboardConfirmDialog.vue +1 -1
  5. package/dist/runtime/components/Dashboard/DashboardGrid.vue +45 -1
  6. package/dist/runtime/components/Dashboard/DashboardNameDialog.vue +1 -1
  7. package/dist/runtime/components/Dashboard/DashboardPickerDialog.vue +1 -1
  8. package/dist/runtime/components/Dashboard/DashboardShareDialog.vue +1 -1
  9. package/dist/runtime/components/Dashboard/DashboardWidgetConfigDialog.vue +1 -1
  10. package/dist/runtime/components/Dashboard/DashboardWidgetFrame.d.vue.ts +2 -2
  11. package/dist/runtime/components/Dashboard/DashboardWidgetFrame.vue +14 -3
  12. package/dist/runtime/components/Dashboard/DashboardWidgetFrame.vue.d.ts +2 -2
  13. package/dist/runtime/components/DataTable/DataTable.d.vue.ts +73 -34
  14. package/dist/runtime/components/DataTable/DataTable.vue +352 -52
  15. package/dist/runtime/components/DataTable/DataTable.vue.d.ts +73 -34
  16. package/dist/runtime/components/DataTable/FacetPanel.vue +57 -8
  17. package/dist/runtime/components/DataTable/FilterMenu.vue +5 -5
  18. package/dist/runtime/components/DataTable/SavedViewsMenu.vue +61 -12
  19. package/dist/runtime/components/DataTable/SortMenu.vue +4 -1
  20. package/dist/runtime/components/DataTable/ToolbarLabelTrigger.d.vue.ts +23 -0
  21. package/dist/runtime/components/DataTable/ToolbarLabelTrigger.vue +29 -0
  22. package/dist/runtime/components/DataTable/ToolbarLabelTrigger.vue.d.ts +23 -0
  23. package/dist/runtime/components/DataTable/types.d.ts +17 -0
  24. package/dist/runtime/components/Form/FieldGrid.vue +1 -1
  25. package/dist/runtime/components/Form/SaveBar.d.vue.ts +1 -1
  26. package/dist/runtime/components/Form/SaveBar.vue.d.ts +1 -1
  27. package/dist/runtime/components/Media/MediaFolderTree.d.vue.ts +4 -4
  28. package/dist/runtime/components/Media/MediaFolderTree.vue +1 -2
  29. package/dist/runtime/components/Media/MediaFolderTree.vue.d.ts +4 -4
  30. package/dist/runtime/components/Media/MediaPickerDialog.d.vue.ts +11 -0
  31. package/dist/runtime/components/Media/MediaPickerDialog.vue +206 -54
  32. package/dist/runtime/components/Media/MediaPickerDialog.vue.d.ts +11 -0
  33. package/dist/runtime/components/Media/MediaUploader.vue +1 -1
  34. package/dist/runtime/components/Shared/AppIcon.d.vue.ts +1 -1
  35. package/dist/runtime/components/Shared/AppIcon.vue.d.ts +1 -1
  36. package/dist/runtime/components/Shared/CardSizeToggle.d.vue.ts +11 -0
  37. package/dist/runtime/components/Shared/CardSizeToggle.vue +19 -0
  38. package/dist/runtime/components/Shared/CardSizeToggle.vue.d.ts +11 -0
  39. package/dist/runtime/components/Shared/DetailSheet.d.vue.ts +1 -1
  40. package/dist/runtime/components/Shared/DetailSheet.vue +9 -5
  41. package/dist/runtime/components/Shared/DetailSheet.vue.d.ts +1 -1
  42. package/dist/runtime/components/Shared/EmptyNote.d.vue.ts +19 -0
  43. package/dist/runtime/components/Shared/EmptyNote.vue +25 -0
  44. package/dist/runtime/components/Shared/EmptyNote.vue.d.ts +19 -0
  45. package/dist/runtime/components/Shared/KpiTile.d.vue.ts +6 -0
  46. package/dist/runtime/components/Shared/KpiTile.vue +10 -9
  47. package/dist/runtime/components/Shared/KpiTile.vue.d.ts +6 -0
  48. package/dist/runtime/components/Shared/PageHeader.vue +15 -4
  49. package/dist/runtime/components/Shared/RecordLayout.vue +1 -2
  50. package/dist/runtime/components/Shared/RecordRailCard.vue +3 -3
  51. package/dist/runtime/components/Shared/SelectionBar.d.vue.ts +22 -0
  52. package/dist/runtime/components/Shared/SelectionBar.vue +40 -0
  53. package/dist/runtime/components/Shared/SelectionBar.vue.d.ts +22 -0
  54. package/dist/runtime/components/Shared/SidebarNav.d.vue.ts +36 -2
  55. package/dist/runtime/components/Shared/SidebarNav.vue +74 -25
  56. package/dist/runtime/components/Shared/SidebarNav.vue.d.ts +36 -2
  57. package/dist/runtime/components/Shared/SidebarNavAreas.d.vue.ts +7 -0
  58. package/dist/runtime/components/Shared/SidebarNavAreas.vue +291 -0
  59. package/dist/runtime/components/Shared/SidebarNavAreas.vue.d.ts +7 -0
  60. package/dist/runtime/components/Shared/SidebarNavLink.d.vue.ts +1 -9
  61. package/dist/runtime/components/Shared/SidebarNavLink.vue +31 -5
  62. package/dist/runtime/components/Shared/SidebarNavLink.vue.d.ts +1 -9
  63. package/dist/runtime/components/Shared/SortHeader.vue +6 -2
  64. package/dist/runtime/components/Shared/StateCard.vue +1 -1
  65. package/dist/runtime/components/Shared/TabBar.vue +6 -7
  66. package/dist/runtime/components/Shared/Wizard.d.vue.ts +11 -1
  67. package/dist/runtime/components/Shared/Wizard.vue +9 -5
  68. package/dist/runtime/components/Shared/Wizard.vue.d.ts +11 -1
  69. package/dist/runtime/composables/useMediaLibrary.d.ts +12 -0
  70. package/dist/runtime/composables/useSavedViews.d.ts +11 -1
  71. package/dist/runtime/composables/useSavedViews.js +4 -4
  72. package/dist/runtime/composables/useWidgetFigures.d.ts +12 -0
  73. package/dist/runtime/composables/useWidgetFigures.js +24 -0
  74. package/dist/runtime/composables/useWidgetHeader.d.ts +15 -0
  75. package/dist/runtime/composables/useWidgetHeader.js +15 -0
  76. package/dist/runtime/lib/auditEnvelope.d.ts +43 -0
  77. package/dist/runtime/lib/auditEnvelope.js +55 -0
  78. package/dist/runtime/lib/cardSize.d.ts +13 -0
  79. package/dist/runtime/lib/cardSize.js +20 -0
  80. package/dist/runtime/lib/dashboardIcons.js +2 -0
  81. package/dist/runtime/lib/dataTableFilters.d.ts +22 -0
  82. package/dist/runtime/lib/dataTableFilters.js +5 -0
  83. package/dist/runtime/lib/facets.d.ts +13 -0
  84. package/dist/runtime/lib/formLayout.d.ts +74 -4
  85. package/dist/runtime/lib/formLayout.js +53 -6
  86. package/dist/runtime/lib/gridTracks.d.ts +14 -1
  87. package/dist/runtime/lib/gridTracks.js +13 -2
  88. package/dist/runtime/lib/isoLists.d.ts +37 -2
  89. package/dist/runtime/lib/isoLists.js +54 -1
  90. package/dist/runtime/lib/notice.d.ts +14 -0
  91. package/dist/runtime/lib/notice.js +19 -0
  92. package/dist/runtime/lib/pageSize.d.ts +15 -0
  93. package/dist/runtime/lib/pageSize.js +26 -0
  94. package/dist/runtime/lib/sidebarAreas.d.ts +46 -0
  95. package/dist/runtime/lib/sidebarAreas.js +25 -0
  96. package/dist/runtime/lib/sidebarNav.d.ts +39 -2
  97. package/dist/runtime/lib/sidebarNav.js +30 -4
  98. package/dist/runtime/lib/solarIconMap.d.ts +2 -1
  99. package/dist/runtime/lib/solarIconMap.js +50 -4
  100. package/dist/runtime/lib/swipeToClose.d.ts +25 -0
  101. package/dist/runtime/lib/swipeToClose.js +118 -0
  102. package/dist/runtime/lib/tableExport.d.ts +31 -0
  103. package/dist/runtime/lib/tableExport.js +143 -0
  104. package/dist/runtime/lib/talkback.d.ts +5 -0
  105. package/dist/runtime/lib/talkback.js +8 -0
  106. package/dist/runtime/lib/talkbackTokenBatch.d.ts +35 -0
  107. package/dist/runtime/lib/talkbackTokenBatch.js +101 -0
  108. package/dist/runtime/types/nav.d.ts +9 -0
  109. package/dist/types.d.mts +4 -0
  110. package/package.json +4 -3
@@ -0,0 +1,19 @@
1
+ const ABBREVIATIONS = /* @__PURE__ */ new Set(["bzw", "usw", "ggf", "inkl", "zzgl", "evtl", "etc", "vgl", "ca", "nr", "max", "min", "abs", "gem", "sog", "e.g", "i.e", "vs"]);
2
+ export function splitNotice(text) {
3
+ const source = (text ?? "").trim();
4
+ if (!source) return { title: "", description: null };
5
+ const pattern = /([.!?])\s+(?=[A-ZÄÖÜ„"(])/g;
6
+ let match;
7
+ while (match = pattern.exec(source)) {
8
+ const end = match.index;
9
+ if (match[1] === ".") {
10
+ const word = /([\p{L}.]+)$/u.exec(source.slice(0, end))?.[1] ?? "";
11
+ const bare = word.replace(/\.$/, "");
12
+ if (bare.length <= 2 || ABBREVIATIONS.has(bare.toLowerCase())) continue;
13
+ }
14
+ const title = source.slice(0, end + 1).trim();
15
+ const description = source.slice(pattern.lastIndex).trim();
16
+ return { title, description: description || null };
17
+ }
18
+ return { title: source, description: null };
19
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Rows per page (PE-1052, Michael 15.10.): 50 by default, 25 / 50 / 100 / 200
3
+ * to choose from, remembered per user and table. The dummy keeps the choice in
4
+ * localStorage; the cockpit's `user_preferences` row is where it goes on
5
+ * transfer (same split as the density toggle).
6
+ */
7
+ export declare const PAGE_SIZE_OPTIONS: readonly [25, 50, 100, 200];
8
+ export declare const DEFAULT_PAGE_SIZE = 50;
9
+ /** The storage key for one table's page size. */
10
+ export declare function pageSizeStorageKey(table: string): string;
11
+ /** The remembered size for a table, if it is one of the offered sizes. */
12
+ export declare function readPageSize(table: string | null | undefined): number | null;
13
+ export declare function writePageSize(table: string | null | undefined, size: number): void;
14
+ /** The sizes the footer offers — the standard four, plus a surface's own if it set another. */
15
+ export declare function pageSizeOptions(current: number): number[];
@@ -0,0 +1,26 @@
1
+ export const PAGE_SIZE_OPTIONS = [25, 50, 100, 200];
2
+ export const DEFAULT_PAGE_SIZE = 50;
3
+ export function pageSizeStorageKey(table) {
4
+ return `cockpit.datatable.pageSize:${table}`;
5
+ }
6
+ export function readPageSize(table) {
7
+ if (!table) return null;
8
+ try {
9
+ if (typeof window === "undefined") return null;
10
+ const n = Number(window.localStorage.getItem(pageSizeStorageKey(table)));
11
+ return PAGE_SIZE_OPTIONS.includes(n) ? n : null;
12
+ } catch {
13
+ return null;
14
+ }
15
+ }
16
+ export function writePageSize(table, size) {
17
+ if (!table) return;
18
+ try {
19
+ if (typeof window === "undefined") return;
20
+ window.localStorage.setItem(pageSizeStorageKey(table), String(size));
21
+ } catch {
22
+ }
23
+ }
24
+ export function pageSizeOptions(current) {
25
+ return [.../* @__PURE__ */ new Set([...PAGE_SIZE_OPTIONS, current])].sort((a, b) => a - b);
26
+ }
@@ -0,0 +1,46 @@
1
+ import type { SidebarNavSection } from '../types/nav.js';
2
+ /**
3
+ * Area mode of the studio sidebar (Commerce Studio, click dummy variant C3).
4
+ *
5
+ * The section the route is in shows its rows as before. Every other section
6
+ * shrinks to a single "area" row; resting on it (or
7
+ * clicking it) lays a panel with that section's rows next to the sidebar.
8
+ * Nothing folds: which section is open follows the route, not a toggle.
9
+ */
10
+ export type SidebarAreaBlock = {
11
+ kind: 'section';
12
+ section: SidebarNavSection;
13
+ } | {
14
+ kind: 'area';
15
+ section: SidebarNavSection;
16
+ };
17
+ /** True when one of the section's rows (or their tabs) holds `path`. */
18
+ export declare function sectionHoldsActive(section: SidebarNavSection, path: string, best?: number): boolean;
19
+ /**
20
+ * A section whose one entry carries the section's own name (Konfiguration →
21
+ * Konfiguration) is that entry: a plain link row, never an area with a panel
22
+ * of one (Michael 17.10.).
23
+ */
24
+ export declare function isSelfNamed(section: SidebarNavSection): boolean;
25
+ /**
26
+ * Which sections render as rows and which as area rows. Only a section without
27
+ * a label (the pinned Dashboard row) and the route's own section show rows;
28
+ * a one-row section is an area too, so every section reads the same way
29
+ * (Michael 17.10.). With `best`, only the section of the closest match opens.
30
+ */
31
+ export declare function sidebarAreaBlocks(sections: SidebarNavSection[], path: string, best?: number): SidebarAreaBlock[];
32
+ export interface Point {
33
+ x: number;
34
+ y: number;
35
+ }
36
+ /**
37
+ * Safe triangle: is `p` inside the triangle spanned by where the pointer last
38
+ * rested on the open area row (`apex`) and the panel's near edge (`top`,
39
+ * `bottom`)? Then the pointer is on its way into the panel and crossing other
40
+ * area rows must not switch the panel (Tog's Apple menus, Amazon's menu-aim).
41
+ */
42
+ export declare function inSafeTriangle(p: Point, apex: Point, top: Point, bottom: Point): boolean;
43
+ /** Timings, in ms. Opening waits for a rest; switching an open panel does not. */
44
+ export declare const AREA_OPEN_DELAY = 150;
45
+ export declare const AREA_AIM_DELAY = 180;
46
+ export declare const AREA_CLOSE_DELAY = 300;
@@ -0,0 +1,25 @@
1
+ import { sidebarItemHoldsActive, sidebarItemMatchLength } from "./sidebarNav.js";
2
+ export function sectionHoldsActive(section, path, best) {
3
+ if (best === void 0) return section.items.some((item) => sidebarItemHoldsActive(item, path));
4
+ return best >= 0 && section.items.some((item) => sidebarItemMatchLength(item, path) === best);
5
+ }
6
+ export function isSelfNamed(section) {
7
+ return section.items.length === 1 && section.items[0].label === section.label;
8
+ }
9
+ export function sidebarAreaBlocks(sections, path, best) {
10
+ return sections.map(
11
+ (section) => !section.label || isSelfNamed(section) || sectionHoldsActive(section, path, best) ? { kind: "section", section } : { kind: "area", section }
12
+ );
13
+ }
14
+ export function inSafeTriangle(p, apex, top, bottom) {
15
+ const side = (a, b, c) => (a.x - c.x) * (b.y - c.y) - (b.x - c.x) * (a.y - c.y);
16
+ const d1 = side(p, apex, top);
17
+ const d2 = side(p, top, bottom);
18
+ const d3 = side(p, bottom, apex);
19
+ const neg = d1 < 0 || d2 < 0 || d3 < 0;
20
+ const pos = d1 > 0 || d2 > 0 || d3 > 0;
21
+ return !(neg && pos);
22
+ }
23
+ export const AREA_OPEN_DELAY = 150;
24
+ export const AREA_AIM_DELAY = 180;
25
+ export const AREA_CLOSE_DELAY = 300;
@@ -1,3 +1,4 @@
1
+ import type { InjectionKey, Ref } from 'vue';
1
2
  import type { SidebarNavItem } from '../types/nav.js';
2
3
  /**
3
4
  * Pure helpers behind `SidebarNav` / `SidebarNavLink`. They live outside the
@@ -12,16 +13,34 @@ import type { SidebarNavItem } from '../types/nav.js';
12
13
  */
13
14
  /** Stable identity for `v-for` keys — `to` is unique where it exists. */
14
15
  export declare function sidebarItemKey(item: SidebarNavItem): string;
15
- /** True when `path` is the item's own route, or sits below it. */
16
+ /**
17
+ * True when `path` is the item's own route, or sits below it. `path` may carry
18
+ * a query: an item whose `to` names one (`/configuration?tab=markets`) is the
19
+ * page with exactly those parameters (an empty value: the parameter is
20
+ * absent); every other item ignores the query.
21
+ */
16
22
  export declare function sidebarItemIsActive(item: SidebarNavItem, path: string): boolean;
17
23
  /** True when the item, or any of its tabs, matches `path`. */
18
24
  export declare function sidebarItemHoldsActive(item: SidebarNavItem, path: string): boolean;
25
+ /**
26
+ * How closely the item (or one of its tabs) matches `path`: the length of the
27
+ * longest matching route, -1 when none does. Two rows can both hold a path —
28
+ * Konfiguration's Zahlung owns `/payments/providers`, Auftragsabwicklung's
29
+ * Zahlungen owns `/payments` — and only the closer one is where you are.
30
+ */
31
+ export declare function sidebarItemMatchLength(item: SidebarNavItem, path: string): number;
32
+ /** The closest match any row of the sidebar has for `path` (-1: none). */
33
+ export declare function sidebarBestMatch(sections: ReadonlyArray<{
34
+ items: SidebarNavItem[];
35
+ }>, path: string): number;
36
+ /** Injected by `SidebarNav`: the closest match of the whole sidebar for the route. */
37
+ export declare const SIDEBAR_BEST_MATCH: InjectionKey<Ref<number>>;
19
38
  /**
20
39
  * Which row gets the highlight. The tabs of a group are not rows of their own,
21
40
  * so the group stays lit for every one of them — an operator on *Carriers*
22
41
  * still sees where they are, which is **Shipping**.
23
42
  */
24
- export declare function sidebarItemIsCurrent(item: SidebarNavItem, path: string): boolean;
43
+ export declare function sidebarItemIsCurrent(item: SidebarNavItem, path: string, best?: number): boolean;
25
44
  /**
26
45
  * Where the row links. A group with a route of its own opens that (it is the
27
46
  * default tab); a pure container — a heading several apps contribute to, where
@@ -32,3 +51,21 @@ export declare function sidebarItemIsCurrent(item: SidebarNavItem, path: string)
32
51
  * section (Settings, Apps, …) from rendering a dead row.
33
52
  */
34
53
  export declare function sidebarItemTarget(item: SidebarNavItem): string | undefined;
54
+ /** A box on screen, as `getBoundingClientRect` reports it — only what the sum needs. */
55
+ interface VerticalBox {
56
+ top: number;
57
+ height: number;
58
+ }
59
+ /**
60
+ * Where the sidebar's scroller has to stand for the current row to be in sight,
61
+ * moving as little as possible: the row's top edge when it sits above the view,
62
+ * its bottom edge when below, and nowhere when it is already visible.
63
+ *
64
+ * This is `scrollIntoView({ block: 'nearest' })` done by hand. The call itself is
65
+ * not usable here: Chrome also moves the point the next Tab starts from to the row
66
+ * it scrolled to, so after a page load the first Tab skipped the host's
67
+ * *Skip to content* and everything before the current row (IN-55). Setting the
68
+ * scroller's `scrollTop` moves the list and leaves the keyboard where it was.
69
+ */
70
+ export declare function sidebarRevealScrollTop(view: VerticalBox, row: VerticalBox, scrollTop: number): number;
71
+ export {};
@@ -3,14 +3,33 @@ export function sidebarItemKey(item) {
3
3
  }
4
4
  export function sidebarItemIsActive(item, path) {
5
5
  if (!item.to) return false;
6
- if (item.exact) return path === item.to;
7
- return path === item.to || path.startsWith(`${item.to}/`);
6
+ const [bare, query = ""] = path.split("?");
7
+ const [to, wanted] = item.to.split("?");
8
+ if (wanted !== void 0) {
9
+ const have = new URLSearchParams(query);
10
+ return bare === to && [...new URLSearchParams(wanted)].every(([k, v]) => (have.get(k) ?? "") === v);
11
+ }
12
+ if (item.exact) return bare === to;
13
+ return bare === to || bare.startsWith(`${to}/`);
8
14
  }
9
15
  export function sidebarItemHoldsActive(item, path) {
10
16
  return sidebarItemIsActive(item, path) || (item.children ?? []).some((child) => sidebarItemHoldsActive(child, path));
11
17
  }
12
- export function sidebarItemIsCurrent(item, path) {
13
- return sidebarItemHoldsActive(item, path);
18
+ export function sidebarItemMatchLength(item, path) {
19
+ const own = sidebarItemIsActive(item, path) ? item.to.length + 0.5 : -1;
20
+ return Math.max(own, ...(item.children ?? []).map((child) => tabMatchLength(child, path)));
21
+ }
22
+ function tabMatchLength(item, path) {
23
+ const own = sidebarItemIsActive(item, path) ? item.to.length : -1;
24
+ return Math.max(own, ...(item.children ?? []).map((child) => tabMatchLength(child, path)));
25
+ }
26
+ export function sidebarBestMatch(sections, path) {
27
+ return Math.max(-1, ...sections.flatMap((section) => section.items.map((item) => sidebarItemMatchLength(item, path))));
28
+ }
29
+ export const SIDEBAR_BEST_MATCH = Symbol("sidebar-best-match");
30
+ export function sidebarItemIsCurrent(item, path, best) {
31
+ if (best === void 0) return sidebarItemHoldsActive(item, path);
32
+ return best >= 0 && sidebarItemMatchLength(item, path) === best;
14
33
  }
15
34
  export function sidebarItemTarget(item) {
16
35
  if (item.to) return item.to;
@@ -20,3 +39,10 @@ export function sidebarItemTarget(item) {
20
39
  }
21
40
  return void 0;
22
41
  }
42
+ export function sidebarRevealScrollTop(view, row, scrollTop) {
43
+ const top = row.top - view.top + scrollTop;
44
+ const bottom = top + row.height;
45
+ if (top < scrollTop) return top;
46
+ if (bottom > scrollTop + view.height) return Math.min(top, bottom - view.height);
47
+ return scrollTop;
48
+ }
@@ -3,11 +3,12 @@
3
3
  *
4
4
  * The closed set of Solar icons the cockpit resolves by name: every icon the
5
5
  * app manifests, this repo's code and the curated picker set ask for. Named
6
- * imports, so the bundle carries these 127 instead of all 1,413
6
+ * imports, so the bundle carries these 150 instead of all 1,413
7
7
  * (PE-985). A name outside the set renders the fallback glyph; run
8
8
  * `pnpm icons` after adding one, and `pnpm icons:check` to verify.
9
9
  */
10
10
  import type { Component } from 'vue';
11
+ import { Record } from '#solar-icons';
11
12
  export declare const SOLAR_ICON_MAP: Readonly<Record<string, Component>>;
12
13
  /** Manifest names Solar does not have → the glyph drawn for them. */
13
14
  export declare const SOLAR_ICON_ALIASES: Readonly<Record<string, string>>;
@@ -3,6 +3,7 @@ import {
3
3
  AddSquare,
4
4
  Alarm,
5
5
  Archive,
6
+ ArrowLeft,
6
7
  ArrowRight,
7
8
  Atom,
8
9
  Backspace,
@@ -16,13 +17,15 @@ import {
16
17
  Bookmark,
17
18
  Box,
18
19
  BranchingPathsUp,
19
- Broom,
20
20
  Buildings,
21
+ Calculator,
21
22
  Calendar,
23
+ CalendarAdd,
24
+ CalendarMark,
22
25
  CalendarMinimalistic,
23
26
  Card,
24
- CardTransfer,
25
27
  Cart,
28
+ CartCheck,
26
29
  CartLarge,
27
30
  CartPlus,
28
31
  Chart,
@@ -32,6 +35,7 @@ import {
32
35
  ChatRoundLine,
33
36
  CheckCircle,
34
37
  CheckRead,
38
+ CheckSquare,
35
39
  ChecklistMinimalistic,
36
40
  Clipboard,
37
41
  ClipboardList,
@@ -48,9 +52,11 @@ import {
48
52
  Download,
49
53
  DownloadMinimalistic,
50
54
  Earth,
55
+ Euro,
51
56
  Eye,
52
57
  File,
53
58
  FileText,
59
+ Flag,
54
60
  Folder,
55
61
  FolderOpen,
56
62
  FolderPathConnect,
@@ -64,6 +70,7 @@ import {
64
70
  Heart,
65
71
  Help,
66
72
  Home,
73
+ Hourglass,
67
74
  Import,
68
75
  Inbox,
69
76
  InboxIn,
@@ -72,12 +79,17 @@ import {
72
79
  Letter,
73
80
  Lightbulb,
74
81
  Link,
82
+ LinkBroken,
83
+ LinkBrokenMinimalistic,
75
84
  LinkRound,
76
85
  List,
77
86
  ListCheck,
78
87
  Lock,
88
+ LockKeyhole,
89
+ Logout,
79
90
  MagicStick,
80
91
  Magnifer,
92
+ Mailbox,
81
93
  Map,
82
94
  MapPoint,
83
95
  MedalRibbon,
@@ -92,6 +104,7 @@ import {
92
104
  PlugCircle,
93
105
  Pulse,
94
106
  Radar,
107
+ Record,
95
108
  Refresh,
96
109
  RefreshCircle,
97
110
  Restart,
@@ -107,20 +120,30 @@ import {
107
120
  ShieldUser,
108
121
  Shop,
109
122
  Shuffle,
123
+ Snowflake,
110
124
  Star,
111
125
  Stars,
126
+ StopCircle,
112
127
  Tag,
113
128
  TagPrice,
129
+ TextField,
114
130
  Ticket,
131
+ TransferHorizontal,
115
132
  TrashBinMinimalistic,
116
133
  TrashBinTrash,
134
+ UndoLeft,
117
135
  UndoLeftRound,
118
136
  Upload,
119
137
  User,
120
138
  UserBlock,
121
139
  UserCheck,
140
+ UserCrossRounded,
141
+ UserHandUp,
122
142
  UserId,
143
+ UserPlus,
144
+ UserPlusRounded,
123
145
  UsersGroupRounded,
146
+ UsersGroupTwoRounded,
124
147
  Wallet,
125
148
  WalletMoney,
126
149
  Weigher,
@@ -132,6 +155,7 @@ export const SOLAR_ICON_MAP = {
132
155
  AddSquare,
133
156
  Alarm,
134
157
  Archive,
158
+ ArrowLeft,
135
159
  ArrowRight,
136
160
  Atom,
137
161
  Backspace,
@@ -145,13 +169,15 @@ export const SOLAR_ICON_MAP = {
145
169
  Bookmark,
146
170
  Box,
147
171
  BranchingPathsUp,
148
- Broom,
149
172
  Buildings,
173
+ Calculator,
150
174
  Calendar,
175
+ CalendarAdd,
176
+ CalendarMark,
151
177
  CalendarMinimalistic,
152
178
  Card,
153
- CardTransfer,
154
179
  Cart,
180
+ CartCheck,
155
181
  CartLarge,
156
182
  CartPlus,
157
183
  Chart,
@@ -161,6 +187,7 @@ export const SOLAR_ICON_MAP = {
161
187
  ChatRoundLine,
162
188
  CheckCircle,
163
189
  CheckRead,
190
+ CheckSquare,
164
191
  ChecklistMinimalistic,
165
192
  Clipboard,
166
193
  ClipboardList,
@@ -177,9 +204,11 @@ export const SOLAR_ICON_MAP = {
177
204
  Download,
178
205
  DownloadMinimalistic,
179
206
  Earth,
207
+ Euro,
180
208
  Eye,
181
209
  File,
182
210
  FileText,
211
+ Flag,
183
212
  Folder,
184
213
  FolderOpen,
185
214
  FolderPathConnect,
@@ -193,6 +222,7 @@ export const SOLAR_ICON_MAP = {
193
222
  Heart,
194
223
  Help,
195
224
  Home,
225
+ Hourglass,
196
226
  Import,
197
227
  Inbox,
198
228
  InboxIn,
@@ -201,12 +231,17 @@ export const SOLAR_ICON_MAP = {
201
231
  Letter,
202
232
  Lightbulb,
203
233
  Link,
234
+ LinkBroken,
235
+ LinkBrokenMinimalistic,
204
236
  LinkRound,
205
237
  List,
206
238
  ListCheck,
207
239
  Lock,
240
+ LockKeyhole,
241
+ Logout,
208
242
  MagicStick,
209
243
  Magnifer,
244
+ Mailbox,
210
245
  Map,
211
246
  MapPoint,
212
247
  MedalRibbon,
@@ -221,6 +256,7 @@ export const SOLAR_ICON_MAP = {
221
256
  PlugCircle,
222
257
  Pulse,
223
258
  Radar,
259
+ Record,
224
260
  Refresh,
225
261
  RefreshCircle,
226
262
  Restart,
@@ -236,20 +272,30 @@ export const SOLAR_ICON_MAP = {
236
272
  ShieldUser,
237
273
  Shop,
238
274
  Shuffle,
275
+ Snowflake,
239
276
  Star,
240
277
  Stars,
278
+ StopCircle,
241
279
  Tag,
242
280
  TagPrice,
281
+ TextField,
243
282
  Ticket,
283
+ TransferHorizontal,
244
284
  TrashBinMinimalistic,
245
285
  TrashBinTrash,
286
+ UndoLeft,
246
287
  UndoLeftRound,
247
288
  Upload,
248
289
  User,
249
290
  UserBlock,
250
291
  UserCheck,
292
+ UserCrossRounded,
293
+ UserHandUp,
251
294
  UserId,
295
+ UserPlus,
296
+ UserPlusRounded,
252
297
  UsersGroupRounded,
298
+ UsersGroupTwoRounded,
253
299
  Wallet,
254
300
  WalletMoney,
255
301
  Weigher,
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Swipe down to close a bottom sheet on the phone (Charge 2, Michael 12.10.).
3
+ *
4
+ * `v-swipe-close="close"` on any element inside a sheet: the sheet (the
5
+ * element marked `data-swipe-sheet`, else the nearest `role="dialog"`, else
6
+ * the element itself) follows the finger downwards; released past a quarter of
7
+ * its height — or flicked — it slides out and `close` runs, otherwise it snaps
8
+ * back. A drag only starts while the scroller under the finger is at its top,
9
+ * so it never fights the sheet's own scrolling. Phone widths only; on a wider
10
+ * screen the listeners do nothing.
11
+ */
12
+ import type { Directive } from 'vue';
13
+ export interface SwipeState {
14
+ cleanup: () => void;
15
+ }
16
+ /** Pure decision, exported for tests: does a drag of `dy` px in `ms` close? */
17
+ export declare function shouldClose(dy: number, ms: number, height: number): boolean;
18
+ export declare function attachSwipeToClose(el: HTMLElement, onClose: () => void): SwipeState;
19
+ type SwipeEl = HTMLElement & {
20
+ __swipeClose?: SwipeState;
21
+ __swipeHandler?: () => void;
22
+ };
23
+ /** `v-swipe-close="handler"` — see the module comment. */
24
+ export declare const vSwipeClose: Directive<SwipeEl, (() => void) | undefined>;
25
+ export {};
@@ -0,0 +1,118 @@
1
+ const PHONE = "(max-width: 767.98px)";
2
+ const CLOSE_RATIO = 0.25;
3
+ const FLICK_VELOCITY = 0.6;
4
+ function sheetOf(el) {
5
+ return el.closest("[data-swipe-sheet]") ?? el.closest('[role="dialog"]') ?? el;
6
+ }
7
+ function scrollerAt(target, sheet) {
8
+ let node = target instanceof HTMLElement ? target : null;
9
+ while (node) {
10
+ const style = getComputedStyle(node);
11
+ if (/(auto|scroll)/.test(style.overflowY) && node.scrollHeight > node.clientHeight) return node;
12
+ if (node === sheet) break;
13
+ node = node.parentElement;
14
+ }
15
+ return null;
16
+ }
17
+ export function shouldClose(dy, ms, height) {
18
+ if (dy <= 0) return false;
19
+ if (dy > height * CLOSE_RATIO) return true;
20
+ return ms > 0 && dy / ms > FLICK_VELOCITY && dy > 24;
21
+ }
22
+ export function attachSwipeToClose(el, onClose) {
23
+ let sheet = null;
24
+ let startY = 0;
25
+ let startT = 0;
26
+ let dy = 0;
27
+ let dragging = false;
28
+ let armed = false;
29
+ const reset = (animate) => {
30
+ if (!sheet) return;
31
+ sheet.style.transition = animate ? "transform 200ms ease-out" : "";
32
+ sheet.style.transform = "";
33
+ };
34
+ const onStart = (e) => {
35
+ if (!window.matchMedia(PHONE).matches || e.touches.length !== 1) return;
36
+ sheet = sheetOf(el);
37
+ const scroller = scrollerAt(e.target, sheet);
38
+ armed = !scroller || scroller.scrollTop <= 0;
39
+ if (!armed) return;
40
+ startY = e.touches[0].clientY;
41
+ startT = performance.now();
42
+ dy = 0;
43
+ dragging = false;
44
+ };
45
+ const onMove = (e) => {
46
+ if (!armed || !sheet) return;
47
+ const delta = e.touches[0].clientY - startY;
48
+ if (!dragging) {
49
+ if (delta < 6) {
50
+ if (delta < -6) armed = false;
51
+ return;
52
+ }
53
+ dragging = true;
54
+ sheet.style.transition = "none";
55
+ }
56
+ dy = Math.max(0, delta);
57
+ sheet.style.transform = `translateY(${dy}px)`;
58
+ if (e.cancelable) e.preventDefault();
59
+ };
60
+ const onEnd = () => {
61
+ if (!armed || !sheet || !dragging) {
62
+ armed = false;
63
+ return;
64
+ }
65
+ armed = false;
66
+ dragging = false;
67
+ const height = sheet.getBoundingClientRect().height || window.innerHeight;
68
+ if (shouldClose(dy, performance.now() - startT, height)) {
69
+ const s = sheet;
70
+ s.style.transition = "transform 180ms ease-in";
71
+ s.style.transform = "translateY(100%)";
72
+ window.setTimeout(() => {
73
+ onClose();
74
+ window.setTimeout(() => {
75
+ s.style.transition = "";
76
+ s.style.transform = "";
77
+ }, 350);
78
+ }, 170);
79
+ } else {
80
+ reset(true);
81
+ }
82
+ };
83
+ const host = () => sheetOf(el);
84
+ let bound = null;
85
+ const bind = () => {
86
+ bound = host();
87
+ bound.addEventListener("touchstart", onStart, { passive: true });
88
+ bound.addEventListener("touchmove", onMove, { passive: false });
89
+ bound.addEventListener("touchend", onEnd);
90
+ bound.addEventListener("touchcancel", onEnd);
91
+ };
92
+ bind();
93
+ return {
94
+ cleanup() {
95
+ if (!bound) return;
96
+ bound.removeEventListener("touchstart", onStart);
97
+ bound.removeEventListener("touchmove", onMove);
98
+ bound.removeEventListener("touchend", onEnd);
99
+ bound.removeEventListener("touchcancel", onEnd);
100
+ bound = null;
101
+ }
102
+ };
103
+ }
104
+ export const vSwipeClose = {
105
+ mounted(el, binding) {
106
+ el.__swipeHandler = binding.value;
107
+ requestAnimationFrame(() => {
108
+ el.__swipeClose = attachSwipeToClose(el, () => el.__swipeHandler?.());
109
+ });
110
+ },
111
+ updated(el, binding) {
112
+ el.__swipeHandler = binding.value;
113
+ },
114
+ beforeUnmount(el) {
115
+ el.__swipeClose?.cleanup();
116
+ delete el.__swipeClose;
117
+ }
118
+ };
@@ -0,0 +1,31 @@
1
+ /**
2
+ * XLSX and CSV writer for the list export (PE-1052, Michael 15.10.). Used where
3
+ * the app declares no Baseline IO export: the header's and the bulk bar's
4
+ * „Exportieren“ then write the file here — current view, all, or the
5
+ * selection, the visible columns in their order.
6
+ *
7
+ * No dependency. XLSX is a zip of five small XML files; a stored (uncompressed)
8
+ * zip is a valid one, and every spreadsheet program reads it. Pulling SheetJS
9
+ * into the shared package for one sheet of text would add 400 KB to every host
10
+ * that renders a DataTable.
11
+ */
12
+ export type ExportCell = string | number | null | undefined;
13
+ export interface ExportSheet {
14
+ headers: string[];
15
+ rows: ExportCell[][];
16
+ /** Sheet tab name, cut to Excel's 31 characters. */
17
+ sheetName?: string;
18
+ }
19
+ /**
20
+ * Semicolon-separated with a UTF-8 BOM: what German Excel opens by double
21
+ * click with umlauts and decimal commas intact. A comma-separated file opens
22
+ * there as one column.
23
+ */
24
+ export declare function toCsv(sheet: ExportSheet, sep?: string): string;
25
+ /** A1-style column letters: 0 → A, 25 → Z, 26 → AA. */
26
+ export declare function columnLetter(index: number): string;
27
+ export declare function crc32(bytes: Uint8Array): number;
28
+ export declare function toXlsx(sheet: ExportSheet): Uint8Array;
29
+ /** „Produkte 2026-10-15“ → `Produkte-2026-10-15`: safe on every file system. */
30
+ export declare function exportFileName(base: string, ext: 'csv' | 'xlsx', now?: Date): string;
31
+ export declare function downloadExport(sheet: ExportSheet, format: 'csv' | 'xlsx', fileBase: string): void;