@pgcorp/ui-kit 0.12.0 → 0.13.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 (42) hide show
  1. package/README.md +62 -0
  2. package/dist/assets/src/components/shared/_internal/SDialogSurface.css_vue_type_style_index_0_src_d1b65adf_scoped_d1b65adf_lang.css +1 -1
  3. package/dist/assets/src/components/shared/_internal/SMenuItem.css_vue_type_style_index_0_src_af485607_scoped_af485607_lang.css +2 -0
  4. package/dist/assets/src/components/shared/containers/{SCollapsiblePanel.css_vue_type_style_index_0_src_4882f962_scoped_4882f962_lang.css → SCollapsiblePanel.css_vue_type_style_index_0_src_71824088_scoped_71824088_lang.css} +1 -1
  5. package/dist/assets/src/components/shared/containers/{SModal.css_vue_type_style_index_0_src_23373daf_scoped_23373daf_lang.css → SModal.css_vue_type_style_index_0_src_0dc51b74_scoped_0dc51b74_lang.css} +1 -1
  6. package/dist/assets/src/components/shared/controls/{SDropdownMenu.css_vue_type_style_index_0_src_b854a89e_scoped_b854a89e_lang.css → SDropdownMenu.css_vue_type_style_index_0_src_1ccb9efc_scoped_1ccb9efc_lang.css} +1 -1
  7. package/dist/assets/src/components/shared/controls/{SInputText.css_vue_type_style_index_0_src_a9c5f1aa_scoped_a9c5f1aa_lang.css → SInputText.css_vue_type_style_index_0_src_023c25d0_scoped_023c25d0_lang.css} +1 -1
  8. package/dist/assets/src/components/shared/controls/SMenuSurface.css_vue_type_style_index_0_src_ddcc5fbb_scoped_ddcc5fbb_lang.css +2 -0
  9. package/dist/src/components/shared/_internal/SMenuItem.vue.js +2 -2
  10. package/dist/src/components/shared/_internal/SMenuItem.vue_vue_type_script_setup_true_lang.js +88 -76
  11. package/dist/src/components/shared/_internal/dialogSurface.d.ts +1 -1
  12. package/dist/src/components/shared/_internal/dialogSurface.js +1 -0
  13. package/dist/src/components/shared/containers/SCollapsiblePanel.vue.d.ts +1 -1
  14. package/dist/src/components/shared/containers/SCollapsiblePanel.vue.js +2 -2
  15. package/dist/src/components/shared/containers/SCollapsiblePanel.vue_vue_type_script_setup_true_lang.js +60 -58
  16. package/dist/src/components/shared/containers/SModal.vue.js +2 -2
  17. package/dist/src/components/shared/containers/SModal.vue_vue_type_script_setup_true_lang.js +43 -37
  18. package/dist/src/components/shared/controls/SDropdownMenu.vue.d.ts +15 -4
  19. package/dist/src/components/shared/controls/SDropdownMenu.vue.js +2 -2
  20. package/dist/src/components/shared/controls/SDropdownMenu.vue_vue_type_script_setup_true_lang.js +98 -62
  21. package/dist/src/components/shared/controls/SInputText.vue.d.ts +2 -2
  22. package/dist/src/components/shared/controls/SInputText.vue.js +2 -2
  23. package/dist/src/components/shared/controls/SInputText.vue_vue_type_script_setup_true_lang.js +16 -25
  24. package/dist/src/components/shared/controls/SMenuSurface.vue.js +2 -2
  25. package/dist/src/components/shared/controls/SMenuSurface.vue_vue_type_script_setup_true_lang.js +132 -110
  26. package/dist/src/components/shared/controls/STextarea.vue.d.ts +4 -0
  27. package/dist/src/components/shared/controls/STextarea.vue_vue_type_script_setup_true_lang.js +154 -147
  28. package/dist/src/components/shared/controls/menu.d.ts +9 -1
  29. package/dist/src/components/shared/controls/menu.js +43 -28
  30. package/dist/src/composables/useDialogOverlay.d.ts +1 -0
  31. package/dist/src/composables/useDialogOverlay.js +5 -2
  32. package/dist/src/internal/textLengthConstraints.d.ts +9 -0
  33. package/dist/src/internal/textLengthConstraints.js +18 -0
  34. package/dist/src/internal/useMenuLayer.d.ts +2 -1
  35. package/dist/src/internal/useMenuLayer.js +20 -14
  36. package/docs/public-api.md +14 -2
  37. package/package.json +1 -1
  38. package/src/components/shared/_internal/SDialogSurface.css +4 -0
  39. package/src/components/shared/_internal/SMenuItem.css +22 -1
  40. package/src/components/shared/controls/SMenuSurface.css +20 -0
  41. package/dist/assets/src/components/shared/_internal/SMenuItem.css_vue_type_style_index_0_src_e5bd192e_scoped_e5bd192e_lang.css +0 -2
  42. package/dist/assets/src/components/shared/controls/SMenuSurface.css_vue_type_style_index_0_src_bc7c7be4_scoped_bc7c7be4_lang.css +0 -2
@@ -24,6 +24,8 @@ function o(o) {
24
24
  }), c.value = { zIndex: u.zIndex }, l.value = { zIndex: `calc(${u.zIndex} + var(--s-layer-overlay-surface-offset))` };
25
25
  }, m = (e = !0) => {
26
26
  u?.unregister({ restoreFocus: e }), u = null, c.value = {}, l.value = {};
27
+ }, h = () => {
28
+ d += 1, m(!1);
27
29
  };
28
30
  return a(o.open, async (e, t) => {
29
31
  d += 1;
@@ -37,12 +39,13 @@ function o(o) {
37
39
  }
38
40
  m(!!t);
39
41
  }, { immediate: !0 }), r(() => {
40
- d += 1, m(!1);
42
+ h();
41
43
  }), {
42
44
  panelRef: s,
43
45
  setPanelElement: f,
44
46
  backdropStyle: c,
45
- surfaceStyle: l
47
+ surfaceStyle: l,
48
+ dispose: h
46
49
  };
47
50
  }
48
51
  //#endregion
@@ -0,0 +1,9 @@
1
+ export interface TextLengthConstraints {
2
+ minLength?: number;
3
+ maxLength?: number;
4
+ }
5
+ /**
6
+ * Проверяет общий native text-length contract без coercion и усечения model.
7
+ * Validates the shared native text-length contract without coercing or truncating the model.
8
+ */
9
+ export declare function resolveTextLengthConstraints(owner: string, minLength: unknown, maxLength: unknown): TextLengthConstraints;
@@ -0,0 +1,18 @@
1
+ import { validateBoundedInteger as e } from "./runtimeContract.js";
2
+ //#region src/internal/textLengthConstraints.ts
3
+ function t(t, n, r) {
4
+ let i = n === void 0 ? void 0 : e(t, "minLength", n, {
5
+ minimum: 0,
6
+ maximum: 2 ** 53 - 1
7
+ }), a = r === void 0 ? void 0 : e(t, "maxLength", r, {
8
+ minimum: 0,
9
+ maximum: 2 ** 53 - 1
10
+ });
11
+ if (i !== void 0 && a !== void 0 && i > a) throw RangeError(`${t}: minLength (${i}) не может превышать maxLength (${a}). / ${t}: minLength (${i}) must not exceed maxLength (${a}).`);
12
+ return {
13
+ minLength: i,
14
+ maxLength: a
15
+ };
16
+ }
17
+ //#endregion
18
+ export { t as resolveTextLengthConstraints };
@@ -3,7 +3,7 @@ export type MenuAnchor = {
3
3
  type: 'element';
4
4
  element: HTMLElement;
5
5
  align: 'left' | 'right';
6
- placement?: 'bottom' | 'inline';
6
+ placement?: 'bottom' | 'top' | 'inline';
7
7
  } | {
8
8
  type: 'point';
9
9
  x: number;
@@ -13,6 +13,7 @@ export type MenuCloseReason = 'escape' | 'outside' | 'programmatic' | 'selection
13
13
  /** Управляет общей lifecycle/focus границей menu поверх canonical floating geometry. / Owns menu lifecycle and focus over canonical floating geometry. */
14
14
  export declare function useMenuLayer(layerRef: Ref<HTMLElement | null>, options?: {
15
15
  onDismiss?: (reason: 'escape' | 'outside') => void;
16
+ onRequestDismiss?: (reason: 'escape' | 'outside') => void;
16
17
  }): {
17
18
  isOpen: Ref<boolean, boolean>;
18
19
  position: Ref<import('../composables/useFloatingPosition').FloatingCoordinates, import('../composables/useFloatingPosition').FloatingCoordinates>;
@@ -23,17 +23,24 @@ function l(l, u = {}) {
23
23
  (d.value || h) && (d.value = !1, h?.unregister({ restoreFocus: e !== "outside" }), h = null, p = null, m = null, f.value = "var(--s-layer-root-floating)");
24
24
  }
25
25
  function _(e) {
26
+ if (u.onRequestDismiss) {
27
+ u.onRequestDismiss(e);
28
+ return;
29
+ }
30
+ g(e), u.onDismiss?.(e);
31
+ }
32
+ function v(e) {
26
33
  let n = m;
27
34
  if (!n) {
28
35
  g("outside");
29
36
  return;
30
37
  }
31
38
  let r = h?.focusScope() ?? document, a = t(n, r, l.value, e.shiftKey);
32
- e.preventDefault(), g("outside"), a && i(() => a.focus());
39
+ e.preventDefault(), _("outside"), a && i(() => a.focus());
33
40
  }
34
- let { coordinates: v, updatePosition: y } = r({
41
+ let { coordinates: y, updatePosition: b } = r({
35
42
  active: d,
36
- placement: () => p?.type === "element" && p.placement === "inline" ? p.align === "right" ? "left-start" : "right-start" : p?.type === "element" && p.align === "right" ? "bottom-end" : "bottom-start",
43
+ placement: () => p?.type === "element" && p.placement === "inline" ? p.align === "right" ? "left-start" : "right-start" : p?.type === "element" && p.placement === "top" ? p.align === "right" ? "top-end" : "top-start" : p?.type === "element" && p.align === "right" ? "bottom-end" : "bottom-start",
37
44
  anchorElement: () => p?.type === "element" ? p.element : null,
38
45
  anchorRect: () => p ? p.type === "point" ? c(p.x, p.y) : p.element.isConnected ? p.element.getBoundingClientRect() : (g("outside"), null) : null,
39
46
  panelRef: l,
@@ -45,7 +52,7 @@ function l(l, u = {}) {
45
52
  zIndex: () => f.value,
46
53
  scrollStrategy: "reposition"
47
54
  });
48
- async function b(t, r) {
55
+ async function x(t, r) {
49
56
  if (d.value && g("outside"), t.type === "element" && e(t.element) !== t.element) throw Error("useMenuLayer: element anchor must be the canonical focusable owner");
50
57
  p = t;
51
58
  let a = e(r ?? null) ?? e(document.activeElement);
@@ -55,26 +62,25 @@ function l(l, u = {}) {
55
62
  anchor: () => t.type === "element" ? t.element : a,
56
63
  restoreTarget: a,
57
64
  requestDismiss: (e) => {
58
- let t = e === "escape" ? "escape" : "outside";
59
- g(t), u.onDismiss?.(t);
65
+ _(e === "escape" ? "escape" : "outside");
60
66
  }
61
67
  }), f.value = h.zIndex, await i(), d.value) {
62
68
  if (!l.value) throw g("outside"), Error("useMenuLayer: layer element is unavailable after opening");
63
- y();
69
+ b();
64
70
  }
65
71
  }
66
- function x(e, t) {
67
- d.value ? g("trigger") : b(e, t);
72
+ function S(e, t) {
73
+ d.value ? g("trigger") : x(e, t);
68
74
  }
69
75
  return a(() => g("outside")), {
70
76
  isOpen: d,
71
- position: v,
77
+ position: y,
72
78
  zIndex: f,
73
- open: b,
79
+ open: x,
74
80
  close: g,
75
- closeForTab: _,
76
- toggle: x,
77
- positionLayer: y
81
+ closeForTab: v,
82
+ toggle: S,
83
+ positionLayer: b
78
84
  };
79
85
  }
80
86
  //#endregion
@@ -62,12 +62,18 @@ callbacks и trust flags не входят в API.
62
62
  `sm/md/lg = 32/36/40 px` и label typography `12/14/14 px`; тот же
63
63
  `density="compact"` у `SInputText` даёт совместимую геометрию поля без
64
64
  consumer CSS.
65
- `SInputText.minLength` и `SInputText.maxLength` передают строгие
65
+ `SInputText.minLength`/`maxLength` и `STextarea.minLength`/`maxLength` передают строгие
66
66
  неотрицательные safe integers нативному textual input; `minLength <= maxLength`
67
67
  проверяется до рендера, а numeric/temporal families отвергают этот контракт.
68
68
  `SModal.headingLevel` и `SDrawer.headingLevel` принимают только `1 | 2 | 3 | 4 |
69
69
  5 | 6`, используют `2` по умолчанию и сохраняют связь видимого заголовка с
70
70
  dialog surface через `aria-labelledby`.
71
+ `SModal.size="4xl"` задаёт responsive maximum `56rem`; при ошибке дочернего
72
+ mount modal освобождает body lock, layer registration и owned teleport nodes.
73
+ `SDropdownMenu` публикует controlled `open`, `defaultOpen`, размещение
74
+ `auto | bottom | top`, `closeOnSelect`, semantic group entries и доступные
75
+ описания пунктов. `SCollapsiblePanel` связывает compact/dense density с
76
+ 32-пиксельной disclosure geometry через публичный API `SButton`.
71
77
  `shared/data-display/SScrollViewport.vue` владеет accessible scroll region,
72
78
  разрешёнными осями, semantic block size, root-only keyboard navigation и
73
79
  ResizeObserver lifecycle. `shared/data-display/scrollViewport` экспортирует
@@ -138,12 +144,18 @@ and trust flags are not part of the API.
138
144
  `sm/md/lg = 32/36/40 px` with `12/14/14 px` label typography. The matching
139
145
  `SInputText.density="compact"` exposes the same field geometry without consumer
140
146
  CSS.
141
- `SInputText.minLength` and `SInputText.maxLength` pass strict non-negative safe
147
+ `SInputText.minLength`/`maxLength` and `STextarea.minLength`/`maxLength` pass strict non-negative safe
142
148
  integers to the native textual input. `minLength <= maxLength` is validated
143
149
  before rendering, and numeric/temporal families reject this contract.
144
150
  `SModal.headingLevel` and `SDrawer.headingLevel` accept `1 | 2 | 3 | 4 | 5 | 6`
145
151
  only, default to `2`, and preserve the visible-title relationship with the
146
152
  dialog surface through `aria-labelledby`.
153
+ `SModal.size="4xl"` provides a responsive `56rem` maximum; a failed child mount
154
+ releases body lock, layer registration, and owned teleport nodes.
155
+ `SDropdownMenu` exposes controlled `open`, `defaultOpen`,
156
+ `auto | bottom | top` placement, `closeOnSelect`, semantic group entries, and
157
+ accessible item descriptions. `SCollapsiblePanel` maps compact/dense density to
158
+ the 32px disclosure geometry through the public `SButton` API.
147
159
  `shared/data-display/SScrollViewport.vue` owns the accessible scroll region,
148
160
  enabled axes, semantic block size, root-only keyboard navigation, and
149
161
  ResizeObserver lifecycle. `shared/data-display/scrollViewport` exports
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pgcorp/ui-kit",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "Typed Vue 3 design system with accessible components, semantic themes, and workbench patterns.",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
@@ -56,6 +56,10 @@
56
56
  max-width: 48rem;
57
57
  }
58
58
 
59
+ .s-dialog-surface[data-presentation='modal'][data-size='4xl'] {
60
+ max-width: 56rem;
61
+ }
62
+
59
63
  .s-dialog-surface[data-presentation='modal'][data-size='wide'] {
60
64
  max-width: 72rem;
61
65
  }
@@ -80,13 +80,34 @@
80
80
  background: currentColor;
81
81
  }
82
82
 
83
- .s-menu-item__label {
83
+ .s-menu-item__text {
84
+ display: flex;
85
+ min-width: 0;
86
+ flex: 1 1 auto;
87
+ flex-direction: column;
88
+ }
89
+
90
+ .s-menu-item__label,
91
+ .s-menu-item__description {
84
92
  min-width: 0;
85
93
  overflow: hidden;
86
94
  text-overflow: ellipsis;
87
95
  white-space: nowrap;
88
96
  }
89
97
 
98
+ .s-menu-item__description {
99
+ color: var(--s-menu-item-secondary-foreground);
100
+ font-size: 0.75rem;
101
+ font-weight: 400;
102
+ line-height: 1rem;
103
+ }
104
+
105
+ .s-menu-item:hover:not([aria-disabled='true']) .s-menu-item__description,
106
+ .s-menu-item:focus-visible .s-menu-item__description,
107
+ .s-menu-item[data-current='true'] .s-menu-item__description {
108
+ color: var(--s-menu-item-state-foreground);
109
+ }
110
+
90
111
  .s-menu-item__meta {
91
112
  @apply ml-auto inline-flex shrink-0 items-center;
92
113
  gap: var(--s-space-control-gap);
@@ -18,3 +18,23 @@
18
18
  .s-menu-surface[data-orientation='horizontal'] {
19
19
  @apply flex max-w-full gap-1 overflow-x-auto;
20
20
  }
21
+
22
+ .s-menu-group {
23
+ min-width: 0;
24
+ }
25
+
26
+ .s-menu-group + .s-menu-group {
27
+ margin-top: var(--s-space-control-gap);
28
+ padding-top: var(--s-space-control-gap);
29
+ border-top: 1px solid var(--s-floating-outline);
30
+ }
31
+
32
+ .s-menu-group__label {
33
+ padding: 0.25rem var(--s-menu-item-inline-padding);
34
+ color: var(--s-menu-item-secondary-foreground);
35
+ font-size: 0.6875rem;
36
+ font-weight: 700;
37
+ line-height: 1rem;
38
+ letter-spacing: 0.04em;
39
+ text-transform: uppercase;
40
+ }
@@ -1,2 +0,0 @@
1
- /*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
2
- @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){[data-v-e5bd192e],[data-v-e5bd192e]:before,[data-v-e5bd192e]:after,[data-v-e5bd192e]::backdrop{--tw-font-weight:initial;--tw-leading:initial}}}.s-menu-item[data-v-e5bd192e]{text-align:left;--tw-font-weight:var(--font-weight-medium,500);width:100%;min-width:0;font-weight:var(--font-weight-medium,500);min-height:var(--s-density-row-default);align-items:center;gap:var(--s-space-control-gap);padding:var(--s-menu-item-block-padding) var(--s-menu-item-inline-padding);border-radius:var(--radius-xs);color:var(--s-menu-item-foreground);appearance:none;cursor:pointer;transition:background-color var(--s-motion-fast) ease, color var(--s-motion-fast) ease;background:0 0;border:0;font-family:inherit;font-size:.875rem;line-height:1.25rem;display:flex}.s-menu-item[data-density=compact][data-v-e5bd192e]{min-height:var(--s-density-row-compact);padding:var(--s-menu-item-block-padding-compact) var(--s-menu-item-inline-padding-compact);font-size:.75rem;line-height:1rem}.s-menu-item[data-v-e5bd192e]:hover:not([aria-disabled=true]),.s-menu-item[data-v-e5bd192e]:focus-visible,.s-menu-item[data-current=true][data-v-e5bd192e]{background:var(--s-menu-item-state-background);color:var(--s-menu-item-state-foreground)}.s-menu-item[data-v-e5bd192e]:focus-visible{outline:var(--s-focus-ring-width) solid var(--s-focus-ring-color);outline-offset:var(--s-focus-ring-inset-offset)}.s-menu-item[aria-disabled=true][data-v-e5bd192e]{color:var(--s-menu-item-disabled-foreground);cursor:not-allowed;opacity:.62}.s-menu-item__main[data-v-e5bd192e]{align-items:center;gap:var(--s-space-control-gap);min-width:0;display:flex}.s-menu-item__state[data-v-e5bd192e]{flex:0 0 var(--s-icon-size-xs);justify-content:center;align-items:center;display:inline-flex}.s-menu-item__state[data-v-e5bd192e]:empty{display:none}.s-menu-item__icon[data-v-e5bd192e],.s-menu-item__state-icon[data-v-e5bd192e],.s-menu-item__submenu-icon[data-v-e5bd192e]{width:var(--s-icon-size-xs);height:var(--s-icon-size-xs);flex:none}.s-menu-item__state-icon[data-v-e5bd192e],.s-menu-item__radio-dot[data-v-e5bd192e]{color:var(--s-menu-item-indicator-foreground)}.s-menu-item__radio-dot[data-v-e5bd192e]{width:calc(var(--s-icon-size-xs) / 2);height:calc(var(--s-icon-size-xs) / 2);border-radius:var(--s-radius-pill);background:currentColor}.s-menu-item__label[data-v-e5bd192e]{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.s-menu-item__meta[data-v-e5bd192e]{align-items:center;gap:var(--s-space-control-gap);flex-shrink:0;margin-left:auto;display:inline-flex}.s-menu-item__badge[data-v-e5bd192e]{display:inline-flex}.s-menu-item__shortcut[data-v-e5bd192e]{text-align:right;font-size:var(--text-xs,.75rem);line-height:var(--tw-leading,var(--text-xs--line-height,calc(1 / .75)));--tw-leading:calc(var(--spacing,.25rem) * 4);line-height:calc(var(--spacing,.25rem) * 4);white-space:nowrap;color:var(--s-menu-item-secondary-foreground)}.s-menu-item:hover:not([aria-disabled=true]) .s-menu-item__shortcut[data-v-e5bd192e],.s-menu-item:focus-visible .s-menu-item__shortcut[data-v-e5bd192e],.s-menu-item[data-current=true] .s-menu-item__shortcut[data-v-e5bd192e]{color:var(--s-menu-item-state-foreground)}.s-menu-item[data-layout=tile][data-v-e5bd192e]{min-width:5rem}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-leading{syntax:"*";inherits:false}
@@ -1,2 +0,0 @@
1
- /*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
2
- .s-menu-surface[data-v-bc7c7be4]{border-radius:var(--radius-xl,1.5rem);padding:calc(var(--spacing,.25rem) * 1.5);margin:0}.s-menu-surface[data-v-bc7c7be4]:focus{--tw-outline-style:none;outline-style:none}.s-menu-surface[data-v-bc7c7be4]{background:var(--s-floating-surface);color:var(--s-floating-foreground);box-shadow:0 0 0 1px var(--s-floating-outline), var(--s-elevation-floating);width:var(--s-menu-surface-width);min-width:var(--s-menu-surface-min-width);max-width:var(--s-menu-surface-max-width)}.s-menu-surface[data-density=compact][data-v-bc7c7be4]{min-width:calc(var(--spacing,.25rem) * 40);border-radius:var(--radius-md,.75rem);padding:var(--spacing,.25rem)}.s-menu-surface[data-orientation=horizontal][data-v-bc7c7be4]{gap:var(--spacing,.25rem);max-width:100%;display:flex;overflow-x:auto}