@xleddyl/443-components-vue 0.10.2 → 0.12.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 (72) hide show
  1. package/CATALOG.md +195 -180
  2. package/README.md +107 -23
  3. package/dist/components/data/Tag.vue +2 -0
  4. package/dist/components/data/Tag.vue.d.ts +2 -0
  5. package/dist/components/data/TagList.vue +239 -0
  6. package/dist/components/data/TagList.vue.d.ts +53 -0
  7. package/dist/components/data/index.d.ts +1 -0
  8. package/dist/components/data/index.mjs +1 -0
  9. package/dist/components/data/types.d.ts +15 -0
  10. package/dist/components/forms/Combobox.vue +33 -4
  11. package/dist/components/forms/Combobox.vue.d.ts +3 -1
  12. package/dist/components/forms/NumberInput.vue.d.ts +1 -1
  13. package/dist/components/forms/PinInput.vue.d.ts +2 -2
  14. package/dist/components/forms/types.d.ts +2 -0
  15. package/dist/components/layout/AppShell.vue +14 -2
  16. package/dist/components/layout/AppShell.vue.d.ts +3 -1
  17. package/dist/components/layout/Sidebar.vue +8 -3
  18. package/dist/components/layout/Sidebar.vue.d.ts +6 -1
  19. package/dist/components/layout/Stepper.vue +148 -0
  20. package/dist/components/layout/Stepper.vue.d.ts +420 -0
  21. package/dist/components/layout/index.d.ts +1 -0
  22. package/dist/components/layout/index.mjs +1 -0
  23. package/dist/components/layout/types.d.ts +13 -0
  24. package/dist/components/overlays/Toaster.vue +10 -0
  25. package/dist/components/overlays/Toaster.vue.d.ts +1 -1
  26. package/dist/components/templates/DashboardLayout.vue +13 -1
  27. package/dist/components/templates/DashboardLayout.vue.d.ts +7 -2
  28. package/dist/components/templates/SettingsModal.vue +95 -4
  29. package/dist/components/templates/SettingsModal.vue.d.ts +6 -1
  30. package/dist/components/templates/types.d.ts +4 -1
  31. package/dist/composables/data.d.ts +1 -0
  32. package/dist/composables/data.mjs +1 -0
  33. package/dist/composables/layout.d.ts +1 -0
  34. package/dist/composables/layout.mjs +1 -0
  35. package/dist/composables/useRouteState.d.ts +13 -0
  36. package/dist/composables/useRouteState.mjs +37 -0
  37. package/dist/composables/useSortable.d.ts +38 -0
  38. package/dist/composables/useSortable.mjs +215 -0
  39. package/dist/grid/DataGrid.vue +7 -1
  40. package/dist/grid/DataGrid.vue.d.ts +8 -1
  41. package/dist/grid/GridToolbar.vue +57 -2
  42. package/dist/grid/GridToolbar.vue.d.ts +13 -2
  43. package/dist/grid/cells/BadgeCell.vue +90 -18
  44. package/dist/grid/cells/BadgeCell.vue.d.ts +14 -14
  45. package/dist/grid/data-grid.css +57 -0
  46. package/dist/grid/spreadsheet.mjs +8 -4
  47. package/dist/grid/types.d.ts +3 -0
  48. package/dist/icons/builtin.d.ts +2 -0
  49. package/dist/icons/builtin.mjs +2 -0
  50. package/dist/internal/data/sortable.d.ts +5 -0
  51. package/dist/internal/data/sortable.mjs +23 -0
  52. package/dist/internal/layout/query.d.ts +6 -0
  53. package/dist/internal/layout/query.mjs +64 -0
  54. package/dist/internal/layout/route.d.ts +9 -2
  55. package/dist/internal/overlays/avoid.d.ts +26 -0
  56. package/dist/internal/overlays/avoid.mjs +134 -0
  57. package/dist/locale/de.mjs +24 -0
  58. package/dist/locale/en.mjs +24 -0
  59. package/dist/locale/it.mjs +24 -0
  60. package/dist/locale/types.d.ts +24 -0
  61. package/dist/nuxt/module.mjs +14 -0
  62. package/dist/nuxt/runtime/layouts/dashboard.vue +9 -1
  63. package/dist/styles/data/tag-list.css +93 -0
  64. package/dist/styles/data.css +1 -0
  65. package/dist/styles/forced-colors.css +10 -0
  66. package/dist/styles/layout/app-shell.css +25 -6
  67. package/dist/styles/layout/sidebar.css +11 -2
  68. package/dist/styles/layout/stepper.css +244 -0
  69. package/dist/styles/layout.css +1 -0
  70. package/dist/styles/overlays/toast.css +17 -12
  71. package/dist/styles/templates/settings-modal.css +5 -1
  72. package/package.json +1 -1
@@ -0,0 +1,134 @@
1
+ import { onScopeDispose, ref, watch } from "vue";
2
+ import { isClient } from "../core/env.mjs";
3
+ import { waitForAnimations } from "../floating/support.mjs";
4
+ const NONE = { left: 0, right: 0, top: 0, bottom: 0 };
5
+ const GAP = 8;
6
+ function sameInsets(a, b) {
7
+ return a.left === b.left && a.right === b.right && a.top === b.top && a.bottom === b.bottom;
8
+ }
9
+ function rootPixels() {
10
+ return parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;
11
+ }
12
+ function drawerSide(dialog) {
13
+ if (!dialog.classList.contains("k-drawer")) return null;
14
+ if (dialog.classList.contains("k-drawer-left")) return "left";
15
+ if (dialog.classList.contains("k-drawer-bottom")) return "bottom";
16
+ return "right";
17
+ }
18
+ function overlapsX(rect, span) {
19
+ return rect.left < span.right && span.left < rect.right;
20
+ }
21
+ function horizontalOf(position) {
22
+ if (position.endsWith("left")) return "left";
23
+ if (position.endsWith("right")) return "right";
24
+ return "center";
25
+ }
26
+ export function useToasterAvoidance(options) {
27
+ const insets = ref(NONE);
28
+ let observer;
29
+ let frame = 0;
30
+ let current = null;
31
+ function sideInsets(dialog, panel, toaster) {
32
+ const side = drawerSide(dialog);
33
+ if (side !== "left" && side !== "right") return null;
34
+ const horizontal = horizontalOf(options.position());
35
+ if (horizontal !== "center" && horizontal !== side) return NONE;
36
+ const style = getComputedStyle(dialog);
37
+ const padding = parseFloat(side === "left" ? style.paddingLeft : style.paddingRight) || 0;
38
+ const occupied = panel.offsetWidth + padding;
39
+ const margin = rootPixels();
40
+ const free = dialog.clientWidth - occupied - 2 * margin;
41
+ if (free < toasterWidth(toaster, dialog.clientWidth)) return null;
42
+ return { ...NONE, [side]: occupied };
43
+ }
44
+ function toasterWidth(toaster, viewport) {
45
+ const width = toaster.offsetWidth || parseFloat(getComputedStyle(toaster).width) || 0;
46
+ return Math.min(width, viewport - 2 * rootPixels());
47
+ }
48
+ function toasterSpan(toaster) {
49
+ const viewport = document.documentElement.clientWidth;
50
+ const margin = rootPixels();
51
+ const width = toasterWidth(toaster, viewport);
52
+ const horizontal = horizontalOf(options.position());
53
+ if (horizontal === "left") return { left: margin, right: margin + width };
54
+ if (horizontal === "right")
55
+ return { left: viewport - margin - width, right: viewport - margin };
56
+ return { left: (viewport - width) / 2, right: (viewport + width) / 2 };
57
+ }
58
+ function verticalInsets(panel, toaster) {
59
+ const bounds = toasterSpan(toaster);
60
+ const viewport = document.documentElement.clientHeight;
61
+ const margin = rootPixels();
62
+ const next = { ...NONE };
63
+ if (options.position().startsWith("top")) {
64
+ const header = panel.querySelector(":scope > header");
65
+ const rect = header?.getBoundingClientRect();
66
+ if (rect && rect.height && overlapsX(rect, bounds) && rect.bottom < viewport / 2)
67
+ next.top = Math.max(0, Math.ceil(rect.bottom + GAP - margin));
68
+ } else {
69
+ const footer = panel.querySelector(":scope > footer");
70
+ const rect = footer?.getBoundingClientRect();
71
+ if (rect && rect.height && overlapsX(rect, bounds) && rect.top > viewport / 2)
72
+ next.bottom = Math.max(0, Math.ceil(viewport - rect.top + GAP - margin));
73
+ }
74
+ return next;
75
+ }
76
+ function compute() {
77
+ frame = 0;
78
+ const dialog = current;
79
+ const toaster = options.toaster.value;
80
+ const panel = dialog?.querySelector(':scope > [class$="-panel"]');
81
+ if (!dialog || !toaster || !panel) {
82
+ if (!dialog && !sameInsets(insets.value, NONE)) insets.value = NONE;
83
+ return;
84
+ }
85
+ const next = sideInsets(dialog, panel, toaster) ?? verticalInsets(panel, toaster);
86
+ if (!sameInsets(next, insets.value)) insets.value = next;
87
+ }
88
+ function schedule() {
89
+ if (!frame) frame = requestAnimationFrame(compute);
90
+ }
91
+ function observe(dialog) {
92
+ observer ??= new ResizeObserver(schedule);
93
+ const panel = dialog.querySelector(':scope > [class$="-panel"]');
94
+ for (const element of [
95
+ dialog,
96
+ panel,
97
+ panel?.querySelector(":scope > header"),
98
+ panel?.querySelector(":scope > footer")
99
+ ])
100
+ if (element) observer.observe(element);
101
+ }
102
+ function disconnect() {
103
+ observer?.disconnect();
104
+ if (frame) cancelAnimationFrame(frame);
105
+ frame = 0;
106
+ }
107
+ watch(
108
+ [options.outlet, options.toaster],
109
+ ([dialog, toaster]) => {
110
+ if (!isClient()) return;
111
+ disconnect();
112
+ current = dialog;
113
+ if (!dialog) {
114
+ insets.value = NONE;
115
+ return;
116
+ }
117
+ observe(dialog);
118
+ if (toaster) observer?.observe(toaster);
119
+ schedule();
120
+ waitForAnimations(dialog).then(() => {
121
+ if (current === dialog) schedule();
122
+ });
123
+ },
124
+ { immediate: true, flush: "post" }
125
+ );
126
+ if (isClient()) {
127
+ window.addEventListener("resize", schedule);
128
+ onScopeDispose(() => {
129
+ window.removeEventListener("resize", schedule);
130
+ disconnect();
131
+ });
132
+ }
133
+ return { insets, refresh: schedule };
134
+ }
@@ -82,6 +82,10 @@ export const de = {
82
82
  columns: "Spalten",
83
83
  resetColumns: "Spalten zur\xFCcksetzen",
84
84
  exportCsv: "CSV exportieren",
85
+ filters: "Filter",
86
+ filtersActive: "Filter | Filter, {count} aktiv | Filter, {count} aktiv",
87
+ resetFilters: "Filter zur\xFCcksetzen",
88
+ showResults: "Ergebnisse anzeigen",
85
89
  rowsPerPage: "Zeilen pro Seite",
86
90
  previousPage: "Vorherige Seite",
87
91
  nextPage: "N\xE4chste Seite",
@@ -92,6 +96,7 @@ export const de = {
92
96
  no: "Nein",
93
97
  label: "Datentabelle",
94
98
  rowCount: "Keine Zeilen | {count} Zeile | {count} Zeilen",
99
+ moreValues: "{count} weitere",
95
100
  checked: "ausgew\xE4hlt",
96
101
  unchecked: "nicht ausgew\xE4hlt",
97
102
  indeterminate: "teilweise ausgew\xE4hlt",
@@ -248,6 +253,25 @@ export const de = {
248
253
  tabs: {
249
254
  dot: "Erfordert Aufmerksamkeit"
250
255
  },
256
+ sortable: {
257
+ moved: "{label} an Position {position} von {count} verschoben"
258
+ },
259
+ tagList: {
260
+ label: "Elemente",
261
+ empty: "Keine Elemente",
262
+ sortHint: "Mit den Pfeiltasten zwischen Elementen wechseln, mit Alt und einer Pfeiltaste ein Element verschieben",
263
+ removeHint: "Entf dr\xFCcken zum Entfernen",
264
+ moveEarlier: "{label} nach vorne verschieben",
265
+ moveLater: "{label} nach hinten verschieben",
266
+ removed: "{label} entfernt",
267
+ added: "{label} hinzugef\xFCgt"
268
+ },
269
+ stepper: {
270
+ label: "Fortschritt",
271
+ position: "Schritt {step} von {count}",
272
+ complete: "abgeschlossen",
273
+ error: "enth\xE4lt Fehler"
274
+ },
251
275
  listPage: {
252
276
  toolbar: "Ergebnisse filtern",
253
277
  emptyTitle: "Noch nichts vorhanden",
@@ -82,6 +82,10 @@ export const en = {
82
82
  columns: "Columns",
83
83
  resetColumns: "Reset columns",
84
84
  exportCsv: "Export CSV",
85
+ filters: "Filters",
86
+ filtersActive: "Filters | Filters, {count} active | Filters, {count} active",
87
+ resetFilters: "Reset filters",
88
+ showResults: "Show results",
85
89
  rowsPerPage: "Rows per page",
86
90
  previousPage: "Previous page",
87
91
  nextPage: "Next page",
@@ -92,6 +96,7 @@ export const en = {
92
96
  no: "No",
93
97
  label: "Data grid",
94
98
  rowCount: "No rows | {count} row | {count} rows",
99
+ moreValues: "{count} more",
95
100
  checked: "checked",
96
101
  unchecked: "not checked",
97
102
  indeterminate: "partially checked",
@@ -248,6 +253,25 @@ export const en = {
248
253
  tabs: {
249
254
  dot: "Needs attention"
250
255
  },
256
+ sortable: {
257
+ moved: "{label} moved to position {position} of {count}"
258
+ },
259
+ tagList: {
260
+ label: "Items",
261
+ empty: "No items",
262
+ sortHint: "Use the arrow keys to move between items, Alt with an arrow key to move an item",
263
+ removeHint: "Press Delete to remove",
264
+ moveEarlier: "Move {label} earlier",
265
+ moveLater: "Move {label} later",
266
+ removed: "{label} removed",
267
+ added: "{label} added"
268
+ },
269
+ stepper: {
270
+ label: "Progress",
271
+ position: "Step {step} of {count}",
272
+ complete: "completed",
273
+ error: "has errors"
274
+ },
251
275
  listPage: {
252
276
  toolbar: "Filter results",
253
277
  emptyTitle: "Nothing here yet",
@@ -82,6 +82,10 @@ export const it = {
82
82
  columns: "Colonne",
83
83
  resetColumns: "Ripristina colonne",
84
84
  exportCsv: "Esporta CSV",
85
+ filters: "Filtri",
86
+ filtersActive: "Filtri | Filtri, {count} attivo | Filtri, {count} attivi",
87
+ resetFilters: "Azzera filtri",
88
+ showResults: "Mostra risultati",
85
89
  rowsPerPage: "Righe per pagina",
86
90
  previousPage: "Pagina precedente",
87
91
  nextPage: "Pagina successiva",
@@ -92,6 +96,7 @@ export const it = {
92
96
  no: "No",
93
97
  label: "Griglia dati",
94
98
  rowCount: "Nessuna riga | {count} riga | {count} righe",
99
+ moreValues: "altri {count}",
95
100
  checked: "selezionato",
96
101
  unchecked: "non selezionato",
97
102
  indeterminate: "parzialmente selezionato",
@@ -248,6 +253,25 @@ export const it = {
248
253
  tabs: {
249
254
  dot: "Richiede attenzione"
250
255
  },
256
+ sortable: {
257
+ moved: "{label} spostato in posizione {position} di {count}"
258
+ },
259
+ tagList: {
260
+ label: "Elementi",
261
+ empty: "Nessun elemento",
262
+ sortHint: "Usa le frecce per spostarti tra gli elementi, Alt con una freccia per spostare un elemento",
263
+ removeHint: "Premi Canc per rimuovere",
264
+ moveEarlier: "Sposta {label} prima",
265
+ moveLater: "Sposta {label} dopo",
266
+ removed: "{label} rimosso",
267
+ added: "{label} aggiunto"
268
+ },
269
+ stepper: {
270
+ label: "Avanzamento",
271
+ position: "Passo {step} di {count}",
272
+ complete: "completato",
273
+ error: "contiene errori"
274
+ },
251
275
  listPage: {
252
276
  toolbar: "Filtra i risultati",
253
277
  emptyTitle: "Ancora niente qui",
@@ -80,6 +80,10 @@ export interface KMessages {
80
80
  columns: string;
81
81
  resetColumns: string;
82
82
  exportCsv: string;
83
+ filters: string;
84
+ filtersActive: string;
85
+ resetFilters: string;
86
+ showResults: string;
83
87
  rowsPerPage: string;
84
88
  previousPage: string;
85
89
  nextPage: string;
@@ -90,6 +94,7 @@ export interface KMessages {
90
94
  no: string;
91
95
  label: string;
92
96
  rowCount: string;
97
+ moreValues: string;
93
98
  checked: string;
94
99
  unchecked: string;
95
100
  indeterminate: string;
@@ -246,6 +251,25 @@ export interface KMessages {
246
251
  tabs: {
247
252
  dot: string;
248
253
  };
254
+ sortable: {
255
+ moved: string;
256
+ };
257
+ tagList: {
258
+ label: string;
259
+ empty: string;
260
+ sortHint: string;
261
+ removeHint: string;
262
+ moveEarlier: string;
263
+ moveLater: string;
264
+ removed: string;
265
+ added: string;
266
+ };
267
+ stepper: {
268
+ label: string;
269
+ position: string;
270
+ complete: string;
271
+ error: string;
272
+ };
249
273
  listPage: {
250
274
  toolbar: string;
251
275
  emptyTitle: string;
@@ -9,6 +9,7 @@ import {
9
9
  addPlugin,
10
10
  addPluginTemplate,
11
11
  addTemplate,
12
+ addTypeTemplate,
12
13
  createResolver,
13
14
  defineNuxtModule,
14
15
  directoryToURL,
@@ -124,6 +125,19 @@ export default defineNuxtModule({
124
125
  addImports({ name: "useDashboard", from: resolver.resolve("./runtime/dashboard") });
125
126
  const layouts = resolveLayouts(options.layouts);
126
127
  if (layouts.length) {
128
+ addTypeTemplate({
129
+ filename: "types/k-page-meta.d.ts",
130
+ getContents: () => [
131
+ `import type { ShellContent } from '@xleddyl/443-components-vue'`,
132
+ `declare module '#app' {`,
133
+ ` interface PageMeta {`,
134
+ ` content?: ShellContent`,
135
+ ` fill?: boolean`,
136
+ ` }`,
137
+ `}`,
138
+ `export {}`
139
+ ].join("\n")
140
+ });
127
141
  nuxt.hook("app:templates", (app) => {
128
142
  for (const layout of layouts) {
129
143
  if (layout.name in app.layouts) continue;
@@ -13,6 +13,7 @@
13
13
  :account-action="account?.action ?? 'settings'"
14
14
  :settings-sections="settings.sections"
15
15
  :settings-presets="settings.presets"
16
+ :settings-route="settings.route"
16
17
  :on-sign-out="signOutTarget ? signOut : void 0"
17
18
  :breadcrumbs="breadcrumbs"
18
19
  :search="dashboard.search"
@@ -20,7 +21,7 @@
20
21
  :collapsible="dashboard.collapsible"
21
22
  :storage-key="dashboard.storageKey"
22
23
  :fluid="dashboard.fluid"
23
- :fill="route.meta.fill === true || Boolean(dashboard.fill)"
24
+ :content="content"
24
25
  >
25
26
  <slot />
26
27
  </DashboardLayout>
@@ -49,6 +50,13 @@ const dashboard = computed(() => config.value.dashboard ?? {});
49
50
  const account = computed(() => state.value.account ?? config.value.account);
50
51
  const settings = computed(() => config.value.settings ?? {});
51
52
  const signOutTarget = computed(() => settings.value.signOutTo);
53
+ const CONTENT_MODES = ["page", "fill", "centered", "bleed"];
54
+ function contentOf(value) {
55
+ return CONTENT_MODES.includes(value) ? value : void 0;
56
+ }
57
+ const content = computed(
58
+ () => contentOf(route.meta.content) ?? (route.meta.fill === true ? "bleed" : void 0) ?? contentOf(dashboard.value.content) ?? (dashboard.value.fill ? "bleed" : "page")
59
+ );
52
60
  function signOut() {
53
61
  if (signOutTarget.value) return navigateTo(signOutTarget.value);
54
62
  }
@@ -0,0 +1,93 @@
1
+ .k-tag-list {
2
+ min-width: 0;
3
+ border-radius: var(--k-radius-md);
4
+ transition:
5
+ background-color var(--k-duration) var(--k-ease),
6
+ box-shadow var(--k-duration) var(--k-ease);
7
+ }
8
+
9
+ .k-tag-list.is-drop-target {
10
+ background: var(--k-accent-soft);
11
+ box-shadow: 0 0 0 1px var(--k-accent-line);
12
+ }
13
+
14
+ .k-tag-list-items {
15
+ display: flex;
16
+ flex-wrap: wrap;
17
+ align-items: center;
18
+ gap: 0.375rem;
19
+ min-width: 0;
20
+ margin: 0;
21
+ padding: 0;
22
+ list-style: none;
23
+ }
24
+
25
+ .k-tag-list-item {
26
+ position: relative;
27
+ display: inline-flex;
28
+ align-items: center;
29
+ gap: 0.125rem;
30
+ max-width: 100%;
31
+ min-width: 0;
32
+ border-radius: var(--k-radius-sm);
33
+ }
34
+
35
+ .k-tag-list.is-sortable .k-tag-list-item {
36
+ cursor: grab;
37
+ touch-action: none;
38
+ user-select: none;
39
+ -webkit-user-select: none;
40
+ }
41
+
42
+ .k-tag-list.is-sortable .k-tag-list-item.is-locked {
43
+ cursor: default;
44
+ }
45
+
46
+ .k-tag-list.is-dragging .k-tag-list-item {
47
+ cursor: grabbing;
48
+ }
49
+
50
+ .k-tag-list-item.is-moving {
51
+ z-index: 1;
52
+ }
53
+
54
+ .k-tag-list-item.is-moving > .k-tag {
55
+ box-shadow: var(--k-shadow-raised);
56
+ scale: 1.03;
57
+ }
58
+
59
+ .k-tag-list.is-sortable .k-tag-list-item:not(.is-moving) > .k-tag {
60
+ transition:
61
+ box-shadow var(--k-duration) var(--k-ease),
62
+ scale var(--k-duration) var(--k-ease);
63
+ }
64
+
65
+ .k-tag-list-item[data-drop='before']::after,
66
+ .k-tag-list-end[data-drop='before']::after {
67
+ content: '';
68
+ position: absolute;
69
+ inset-block: 0.125rem;
70
+ inset-inline-start: -0.25rem;
71
+ width: 2px;
72
+ border-radius: var(--k-radius-full);
73
+ background: var(--k-ring);
74
+ }
75
+
76
+ .k-tag-list-end {
77
+ position: relative;
78
+ align-self: stretch;
79
+ width: 0;
80
+ min-height: 1.625rem;
81
+ margin-inline-start: 0.25rem;
82
+ }
83
+
84
+ .k-tag-list-move {
85
+ display: inline-flex;
86
+ }
87
+
88
+ .k-tag-list-empty {
89
+ margin: 0;
90
+ padding: 0.25rem 0;
91
+ font-size: 0.8125rem;
92
+ color: var(--k-text-muted);
93
+ }
@@ -2,6 +2,7 @@
2
2
  @import './data/pagination.css';
3
3
  @import './data/badge.css';
4
4
  @import './data/tag.css';
5
+ @import './data/tag-list.css';
5
6
  @import './data/avatar.css';
6
7
  @import './data/description-list.css';
7
8
  @import './data/stat.css';
@@ -139,6 +139,7 @@
139
139
  .k-navlink.is-active,
140
140
  .k-settings-modal-tab[aria-current='true'],
141
141
  .k-tab[aria-selected='true'],
142
+ .k-stepper-item.is-current .k-stepper-indicator,
142
143
  .k-theme-switch-option.is-active,
143
144
  .k-color-input-preset[aria-checked='true'],
144
145
  .k-tree-row[aria-selected='true'],
@@ -221,4 +222,13 @@
221
222
  forced-color-adjust: none;
222
223
  background: CanvasText;
223
224
  }
225
+
226
+ .k-tag-list.is-drop-target {
227
+ outline: 2px dashed Highlight;
228
+ }
229
+
230
+ :is(.k-tag-list-item, .k-tag-list-end)[data-drop='before']::after {
231
+ forced-color-adjust: none;
232
+ background: Highlight;
233
+ }
224
234
  }
@@ -1,5 +1,5 @@
1
1
  .k-shell {
2
- --k-shell-sidebar: var(--k-sidebar-width, 15rem);
2
+ --k-shell-sidebar: var(--k-sidebar-width, 16.5rem);
3
3
  --k-shell-rail: var(--k-sidebar-rail-width, 3.5rem);
4
4
  --k-shell-topbar: var(--k-topbar-height, 3rem);
5
5
  --k-shell-gap: var(--k-shell-inset, 0.5rem);
@@ -106,7 +106,7 @@ html.k-root body {
106
106
  width: 100%;
107
107
  max-width: var(--k-shell-content, 72rem);
108
108
  margin-inline: auto;
109
- padding: var(--k-shell-padding, 2.25rem 2.5rem 2.5rem);
109
+ padding: var(--k-shell-padding, 1.5rem 1.75rem 1.75rem);
110
110
  animation: k-enter 320ms var(--k-ease-out) backwards;
111
111
  }
112
112
 
@@ -126,6 +126,29 @@ html.k-root body {
126
126
  padding: 0;
127
127
  }
128
128
 
129
+ .k-shell:is([data-content='fill'], [data-content='centered']) .k-shell-body {
130
+ overflow: hidden;
131
+ }
132
+
133
+ .k-shell:is([data-content='fill'], [data-content='centered']) .k-shell-main {
134
+ min-height: 0;
135
+ }
136
+
137
+ .k-shell-content:is(.k-shell-content-fill, .k-shell-content-centered) {
138
+ min-height: 0;
139
+ overflow-y: auto;
140
+ overscroll-behavior: contain;
141
+ }
142
+
143
+ .k-shell-content.k-shell-content-fill {
144
+ max-width: none;
145
+ }
146
+
147
+ .k-shell-content:is(.k-shell-content-fill, .k-shell-content-centered) > .k-page {
148
+ flex: 1;
149
+ min-height: 0;
150
+ }
151
+
129
152
  .k-shell[data-variant='sidebar'] .k-shell-sidebar {
130
153
  border-right: 1px solid var(--k-line);
131
154
  }
@@ -199,10 +222,6 @@ html.k-root body {
199
222
  padding-inline: 0;
200
223
  }
201
224
 
202
- .k-shell.is-collapsed .k-sidebar-collapse > .k-icon {
203
- transform: rotate(180deg);
204
- }
205
-
206
225
  @media (hover: hover) {
207
226
  .k-shell.is-collapsed .k-sidebar-header:has(> .k-sidebar-brand:not(:empty)) {
208
227
  position: relative;
@@ -22,8 +22,17 @@
22
22
  padding: 0.25rem 0.375rem;
23
23
  }
24
24
 
25
- .k-sidebar-collapse > .k-icon {
26
- transition: transform var(--k-duration-lg) var(--k-ease-out);
25
+ .k-sidebar-header.is-brand-center {
26
+ position: relative;
27
+ justify-content: center;
28
+ padding-inline: 2rem;
29
+ }
30
+
31
+ .k-sidebar-header.is-brand-center > .k-sidebar-collapse {
32
+ position: absolute;
33
+ top: 50%;
34
+ right: 0;
35
+ translate: 0 -50%;
27
36
  }
28
37
 
29
38
  .k-sidebar-nav {