@pgcorp/ui-kit 0.3.0 → 0.4.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 (39) hide show
  1. package/README.md +36 -0
  2. package/docs/public-api.md +8 -0
  3. package/package.json +1 -1
  4. package/src/components/layout/SAppShell.vue +3 -2
  5. package/src/components/shared/complex/STree.vue +29 -4
  6. package/src/components/shared/complex/treeAdapter.ts +2 -1
  7. package/src/components/shared/complex/types.ts +1 -12
  8. package/src/components/shared/containers/SLeftSidebar.vue +7 -2
  9. package/src/components/shared/containers/SPageHeader.css +5 -0
  10. package/src/components/shared/containers/SPageHeader.vue +1 -1
  11. package/src/components/shared/containers/SPopover.vue +2 -1
  12. package/src/components/shared/containers/SSidebarGroup.vue +10 -5
  13. package/src/components/shared/containers/sidebar.ts +2 -1
  14. package/src/components/shared/controls/SButton.vue +10 -5
  15. package/src/components/shared/controls/SCombobox.vue +47 -5
  16. package/src/components/shared/controls/SInteractiveSurface.vue +2 -1
  17. package/src/components/shared/controls/SLink.ts +1 -17
  18. package/src/components/shared/controls/SLink.vue +26 -2
  19. package/src/components/shared/data-display/SDocBlock.vue +10 -10
  20. package/src/components/shared/data-display/SLinkedSystemsList.vue +3 -3
  21. package/src/components/shared/data-display/STable.css +39 -0
  22. package/src/components/shared/data-display/STable.vue +454 -67
  23. package/src/components/shared/data-display/STooltip.vue +2 -1
  24. package/src/components/shared/data-display/SVirtualList.ts +1 -16
  25. package/src/components/shared/data-display/SVirtualList.vue +18 -2
  26. package/src/components/shared/data-display/json.ts +1 -1
  27. package/src/components/shared/data-display/table.ts +4 -29
  28. package/src/components/shared/database/SDataGrid.vue +29 -8
  29. package/src/components/shared/database/SSqlEditor.vue +3 -2
  30. package/src/components/shared/database/dataGrid.ts +1 -17
  31. package/src/components/shared/navigation/STabs.vue +2 -1
  32. package/src/composables/uiPreferencesReset.ts +2 -1
  33. package/src/composables/useClipboard.ts +2 -1
  34. package/src/composables/useSidebarPanelState.ts +3 -2
  35. package/src/internal/es2020.ts +50 -0
  36. package/src/internal/inlineTokenEditorContract.ts +4 -3
  37. package/src/internal/pointerInteractionLease.ts +2 -1
  38. package/src/internal/semanticSizing.ts +2 -2
  39. package/src/styles/tokens.css +1 -0
@@ -69,6 +69,7 @@ import {
69
69
  type VNodeRef,
70
70
  } from 'vue';
71
71
  import { mergeIdReferences, useOwnedAttrs } from '../../../internal/ownedAttrs';
72
+ import { hasOwn } from '../../../internal/es2020';
72
73
  import {
73
74
  resolveInteractiveElement,
74
75
  type ElementRefTarget,
@@ -202,7 +203,7 @@ function assertDenseArray(coordinate: string, value: unknown): readonly unknown[
202
203
  throw triggerContractError(`${coordinate} должен быть массивом. ${coordinate} must be an array.`);
203
204
  }
204
205
  for (let index = 0; index < value.length; index += 1) {
205
- if (!Object.hasOwn(value, index)) {
206
+ if (!hasOwn(value, index)) {
206
207
  throw triggerContractError(`${coordinate}[${index}] отсутствует в sparse array. ${coordinate}[${index}] is missing from a sparse array.`);
207
208
  }
208
209
  }
@@ -1,19 +1,4 @@
1
1
  /** Ключ элемента виртуального списка. / Virtual-list item key. */
2
2
  export type SVirtualListItemKey = string | number
3
3
 
4
- /**
5
- * Публичный контракт виртуального списка с измеряемыми строками.
6
- * Public contract for a virtual list with measured rows.
7
- */
8
- export interface SVirtualListProps<Item = unknown> {
9
- items: readonly Item[]
10
- /** Оценочная высота строки до фактического измерения ResizeObserver. / Estimated row height before ResizeObserver measurement. */
11
- itemSize: number
12
- overscan?: number
13
- height?: number | null
14
- /** Устойчивый доменный ключ; индекс массива не является допустимым fallback. / Stable domain key; an array index is not a valid fallback. */
15
- itemKey: (item: Item) => SVirtualListItemKey
16
- itemLabel: (item: Item, index: number) => string
17
- accessibleLabel: string
18
- externalScrollTarget?: HTMLElement | null
19
- }
4
+ export type { SVirtualListProps } from './SVirtualList.vue'
@@ -35,6 +35,24 @@
35
35
  </div>
36
36
  </template>
37
37
 
38
+ <script lang="ts">
39
+ import type { SVirtualListItemKey } from './SVirtualList'
40
+
41
+ /** Публичный контракт virtual list принадлежит source-SFC runtime. / The public virtual-list contract is owned by the source-SFC runtime. */
42
+ export interface SVirtualListProps<Item = unknown> {
43
+ items: readonly Item[]
44
+ /** Оценочная высота строки до фактического измерения. / Estimated row height before measurement. */
45
+ itemSize: number
46
+ overscan?: number
47
+ height?: number | null
48
+ /** Устойчивый доменный ключ; индекс не является fallback. / Stable domain key; the index is not a fallback. */
49
+ itemKey: (item: Item) => SVirtualListItemKey
50
+ itemLabel: (item: Item, index: number) => string
51
+ accessibleLabel: string
52
+ externalScrollTarget?: HTMLElement | null
53
+ }
54
+ </script>
55
+
38
56
  <script setup lang="ts" generic="T">
39
57
  import {
40
58
  computed,
@@ -47,8 +65,6 @@ import {
47
65
  } from 'vue'
48
66
  import { useOwnedAttrs } from '../../../internal/ownedAttrs'
49
67
  import { useInteractiveLeafRegistration } from '../../../internal/passiveContentContract'
50
- import type { SVirtualListItemKey, SVirtualListProps } from './SVirtualList'
51
-
52
68
  defineOptions({ inheritAttrs: false })
53
69
 
54
70
  type Alignment = 'auto' | 'start' | 'center' | 'end'
@@ -12,7 +12,7 @@ export type JsonValue = JsonScalar | JsonObject | JsonArray
12
12
 
13
13
  /** Escapes one RFC 6901 reference token. / Экранирует один reference token по RFC 6901. */
14
14
  export function escapeJsonPointerToken(token: string): string {
15
- return token.replaceAll('~', '~0').replaceAll('/', '~1')
15
+ return token.replace(/~/gu, '~0').replace(/\//gu, '~1')
16
16
  }
17
17
 
18
18
  /** Appends a reference token to an RFC 6901 JSON Pointer. / Добавляет reference token к JSON Pointer по RFC 6901. */
@@ -1,5 +1,4 @@
1
1
  import type {
2
- ScrollableViewportMaxBlockSize,
3
2
  TableColumnInlineSize,
4
3
  TableColumnMinInlineSize,
5
4
  } from '../../../internal/semanticSizing'
@@ -12,6 +11,8 @@ export type STableSurface = 'bordered' | 'plain'
12
11
  export type STableMinWidth = 'content' | 'md' | 'lg'
13
12
  export type STableDensity = 'default' | 'compact'
14
13
  export type STableColumnContent = 'field' | 'value' | 'slot'
14
+ export type STableColumnHeaderPresentation = 'visible' | 'assistive'
15
+ export type STableSelectionMode = 'none' | 'multiple'
15
16
  export type STableSortDirection = 'ascending' | 'descending'
16
17
 
17
18
  /** Controlled sort state; consumer owns data ordering. / Управляемое состояние сортировки; порядок данных принадлежит consumer. */
@@ -23,6 +24,7 @@ export interface STableSort<Field extends string = string> {
23
24
  interface STableColumnBase<Field extends string> {
24
25
  field: Field
25
26
  header: string
27
+ headerPresentation?: STableColumnHeaderPresentation
26
28
  width?: TableColumnInlineSize
27
29
  minWidth?: TableColumnMinInlineSize
28
30
  align?: STableAlignment
@@ -55,31 +57,4 @@ export type STableColumn<
55
57
  | STableSlotColumn<Field>
56
58
  | (Field extends Extract<keyof Row, string> ? STableFieldColumn<Field> : never)
57
59
 
58
- /** Публичный generic-контракт таблицы. / Public generic table contract. */
59
- export interface STableProps<
60
- Row extends object,
61
- RowKey extends STableRowKey,
62
- Field extends string,
63
- > {
64
- columns: readonly STableColumn<Row, Field>[]
65
- data: readonly Row[]
66
- getRowKey: (row: Row) => RowKey
67
- loading?: boolean
68
- loadingMessage?: string
69
- errorMessage?: string
70
- emptyMessage?: string
71
- selectedRowKey?: RowKey
72
- sort?: STableSort<Field> | null
73
- maxHeight?: ScrollableViewportMaxBlockSize
74
- density?: STableDensity
75
- surface?: STableSurface
76
- minWidth?: STableMinWidth
77
- rowState?: (row: Row) => STableRowState
78
- cellState?: (row: Row, column: STableColumn<Row, Field>) => STableCellState
79
- /** Accessible name без visible owner. / Accessible name when there is no visible owner. */
80
- ariaLabel?: string
81
- /** DOM id visible owner, mutually exclusive with ariaLabel. / DOM id of the visible owner, mutually exclusive with ariaLabel. */
82
- ariaLabelledby?: string
83
- /** DOM id supplemental status/diagnostic owner. / DOM id of a supplemental status/diagnostic owner. */
84
- ariaDescribedby?: string
85
- }
60
+ export type { STableProps } from './STable.vue'
@@ -83,6 +83,13 @@
83
83
  </template>
84
84
 
85
85
  <script lang="ts">
86
+ import type { ScrollableViewportMaxBlockSize } from '../../../internal/semanticSizing'
87
+ import type {
88
+ SDataGridColumn,
89
+ SDataGridColumnKey,
90
+ SDataGridRow,
91
+ } from './dataGrid'
92
+
86
93
  export type {
87
94
  SDataGridCell,
88
95
  SDataGridCellKind,
@@ -90,14 +97,32 @@ export type {
90
97
  SDataGridColumnKey,
91
98
  SDataGridCommitInput,
92
99
  SDataGridEditPayload,
93
- SDataGridProps,
94
100
  SDataGridRow,
95
101
  SDataGridRowState,
96
102
  } from './dataGrid'
103
+
104
+ /** Публичный контракт data grid принадлежит source-SFC runtime. / The public data-grid contract is owned by the source-SFC runtime. */
105
+ export interface SDataGridProps<Key extends SDataGridColumnKey = SDataGridColumnKey> {
106
+ columns: readonly SDataGridColumn<Key>[]
107
+ rows: readonly SDataGridRow<Key>[]
108
+ title?: string
109
+ loading?: boolean
110
+ disabled?: boolean
111
+ readOnly?: boolean
112
+ errorMessage?: string
113
+ emptyMessage?: string
114
+ selectedRowId?: string
115
+ maxHeight?: ScrollableViewportMaxBlockSize
116
+ /** Accessible name без visible title. / Accessible name when there is no visible title. */
117
+ ariaLabel?: string
118
+ /** DOM id external visible title. / DOM id of an external visible title. */
119
+ ariaLabelledby?: string
120
+ }
97
121
  </script>
98
122
 
99
123
  <script setup lang="ts" generic="ColumnKey extends string = string">
100
124
  import { computed, reactive, useId, useSlots, watch } from 'vue'
125
+ import { hasOwn } from '../../../internal/es2020'
101
126
  import { useOwnedAttrs } from '../../../internal/ownedAttrs'
102
127
  import {
103
128
  validateScrollableViewportMaxBlockSize,
@@ -121,12 +146,8 @@ import type {
121
146
  } from '../data-display/table'
122
147
  import type {
123
148
  SDataGridCell,
124
- SDataGridColumn,
125
- SDataGridColumnKey,
126
149
  SDataGridCommitInput,
127
150
  SDataGridEditPayload,
128
- SDataGridProps,
129
- SDataGridRow,
130
151
  } from './dataGrid'
131
152
 
132
153
  defineOptions({ inheritAttrs: false })
@@ -266,7 +287,7 @@ const validatedGrid = computed<ValidatedGrid<ColumnKey>>(() => {
266
287
  }
267
288
  }
268
289
  for (const [key, column] of columnByKey) {
269
- if (!Object.hasOwn(row.cells, key)) {
290
+ if (!hasOwn(row.cells, key)) {
270
291
  throw new TypeError(
271
292
  `SDataGrid: row ${JSON.stringify(rowId)} не содержит cell ${JSON.stringify(key)}. `
272
293
  + `/ SDataGrid: row ${JSON.stringify(rowId)} is missing cell ${JSON.stringify(key)}.`,
@@ -358,7 +379,7 @@ function resolveColumn(key: string): SDataGridColumn<ColumnKey> {
358
379
 
359
380
  function resolveCell(row: SDataGridRow<ColumnKey>, columnKeyValue: string): SDataGridCell {
360
381
  const columnKey = resolveColumn(columnKeyValue).key
361
- if (Object.hasOwn(row.cells, columnKey)) return row.cells[columnKey]
382
+ if (hasOwn(row.cells, columnKey)) return row.cells[columnKey]
362
383
  throw new TypeError(
363
384
  `SDataGrid: row ${JSON.stringify(row.id)} не содержит cell ${JSON.stringify(columnKey)}. `
364
385
  + `/ SDataGrid: row ${JSON.stringify(row.id)} is missing cell ${JSON.stringify(columnKey)}.`,
@@ -404,7 +425,7 @@ function editCell(row: SDataGridRow<ColumnKey>, column: SDataGridColumn<ColumnKe
404
425
  function validateCommitInput(input: unknown): SDataGridCommitInput {
405
426
  assertRecord(input, 'commit input')
406
427
  const source = validateExactString('SDataGrid', 'commit input.source', input.source, COMMIT_SOURCES)
407
- const hasValue = Object.hasOwn(input, 'value')
428
+ const hasValue = hasOwn(input, 'value')
408
429
  if (source === 'draft' && hasValue) {
409
430
  throw new TypeError(
410
431
  'SDataGrid: draft commit не должен содержать value. / SDataGrid: draft commit must not contain value.',
@@ -51,6 +51,7 @@
51
51
 
52
52
  <script setup lang="ts">
53
53
  import { computed, ref, useId, useSlots } from 'vue'
54
+ import { hasOwn } from '../../../internal/es2020'
54
55
  import { useOwnedAttrs } from '../../../internal/ownedAttrs'
55
56
  import { provideTrustedCodeEditorExtensions } from '../../../internal/codeEditorExtensions'
56
57
  import { validateCodeEditorViewport } from '../../../internal/codeEditorContract'
@@ -291,8 +292,8 @@ const validatedDiagnostics = computed<SSqlEditorDiagnostic[]>(() => {
291
292
  return props.diagnostics.map((candidate, index) => {
292
293
  const runtimeCandidate: unknown = candidate
293
294
  const record = assertPlainExactRecord(`SSqlEditor.diagnostics[${index}]`, runtimeCandidate, ['severity', 'message', 'from', 'to', 'line', 'column'])
294
- const hasOffset = Object.hasOwn(record, 'from')
295
- const hasLine = Object.hasOwn(record, 'line') || Object.hasOwn(record, 'column')
295
+ const hasOffset = hasOwn(record, 'from')
296
+ const hasLine = hasOwn(record, 'line') || hasOwn(record, 'column')
296
297
  if (hasOffset === hasLine) throw new TypeError(`SSqlEditor: diagnostics[${index}] должен задавать ровно один range: from/to или line/column. / SSqlEditor: diagnostics[${index}] must provide exactly one range: from/to or line/column.`)
297
298
  const allowedRangeKeys = hasOffset
298
299
  ? ['severity', 'message', 'from', 'to']
@@ -1,5 +1,4 @@
1
1
  import type {
2
- ScrollableViewportMaxBlockSize,
3
2
  TableColumnInlineSize,
4
3
  TableColumnMinInlineSize,
5
4
  } from '../../../internal/semanticSizing'
@@ -43,19 +42,4 @@ export type SDataGridCommitInput =
43
42
  | { source: 'draft' }
44
43
  | { source: 'value'; value: unknown }
45
44
 
46
- export interface SDataGridProps<Key extends SDataGridColumnKey = SDataGridColumnKey> {
47
- columns: readonly SDataGridColumn<Key>[]
48
- rows: readonly SDataGridRow<Key>[]
49
- title?: string
50
- loading?: boolean
51
- disabled?: boolean
52
- readOnly?: boolean
53
- errorMessage?: string
54
- emptyMessage?: string
55
- selectedRowId?: string
56
- maxHeight?: ScrollableViewportMaxBlockSize
57
- /** Accessible name без visible title. / Accessible name when there is no visible title. */
58
- ariaLabel?: string
59
- /** DOM id external visible title. / DOM id of an external visible title. */
60
- ariaLabelledby?: string
61
- }
45
+ export type { SDataGridProps } from './SDataGrid.vue'
@@ -58,6 +58,7 @@ import {
58
58
  type VNodeChild,
59
59
  } from 'vue'
60
60
  import { useOwnedAttrs } from '../../../internal/ownedAttrs'
61
+ import { lastItem } from '../../../internal/es2020'
61
62
  import {
62
63
  tabsContextKey,
63
64
  type STabListRegistrationKey,
@@ -425,7 +426,7 @@ function handleTabKeydown(key: STabRegistrationKey, event: KeyboardEvent): void
425
426
  }
426
427
  let next: TabRecord | undefined
427
428
  if (event.key === 'Home') next = enabled[0]
428
- else if (event.key === 'End') next = enabled.at(-1)
429
+ else if (event.key === 'End') next = lastItem(enabled)
429
430
  else if (event.key === 'ArrowLeft') next = enabled[(currentIndex - 1 + enabled.length) % enabled.length]
430
431
  else if (event.key === 'ArrowRight') next = enabled[(currentIndex + 1) % enabled.length]
431
432
  if (!next) return
@@ -45,8 +45,9 @@ export function createUiPreferencesResetController(): UiPreferencesResetControll
45
45
  throw errors[0];
46
46
  }
47
47
  if (errors.length > 1) {
48
- throw new AggregateError(errors, 'Не все owners UI-предпочтений смогли обработать сброс.');
48
+ throw createAggregateError(errors, 'Не все owners UI-предпочтений смогли обработать сброс.');
49
49
  }
50
50
  },
51
51
  };
52
52
  }
53
+ import { createAggregateError } from '../internal/es2020';
@@ -28,10 +28,11 @@ export function useClipboard(): ClipboardService {
28
28
  const isDomException = typeof DOMException !== 'undefined' && cause instanceof DOMException
29
29
  const error = cause instanceof Error || isDomException
30
30
  ? cause
31
- : new Error('Clipboard API rejected writeText without an Error object', { cause })
31
+ : withErrorCause(new Error('Clipboard API rejected writeText without an Error object'), cause)
32
32
  return { status: 'failed', error }
33
33
  }
34
34
  }
35
35
 
36
36
  return { copyText }
37
37
  }
38
+ import { withErrorCause } from '../internal/es2020'
@@ -1,4 +1,5 @@
1
1
  import { computed, getCurrentScope, onScopeDispose, reactive, readonly, watch } from 'vue';
2
+ import { hasOwn, lastItem } from '../internal/es2020';
2
3
 
3
4
  import {
4
5
  assertSidebarSectionsState,
@@ -284,7 +285,7 @@ function preferredFlexibleSectionKey(
284
285
  return config?.defaultIsOpen === true && config.defaultSize === null;
285
286
  });
286
287
  if (defaultFlexibleKey && state[defaultFlexibleKey]?.isOpen) return defaultFlexibleKey;
287
- return openKeys.at(-1) ?? null;
288
+ return lastItem(openKeys) ?? null;
288
289
  }
289
290
 
290
291
  /**
@@ -327,7 +328,7 @@ function decodePersistedDocument(
327
328
  if (!isPlainRecord(value)) {
328
329
  throw new SidebarPanelStateContractError('$', 'expected an object');
329
330
  }
330
- if (Object.hasOwn(value, 'version') || Object.hasOwn(value, 'sections')) {
331
+ if (hasOwn(value, 'version') || hasOwn(value, 'sections')) {
331
332
  assertExactKeys(value, ['sections', 'version'], '$');
332
333
  if (value.version !== SIDEBAR_PANEL_STATE_SCHEMA_VERSION) {
333
334
  throw new SidebarPanelStateContractError(
@@ -0,0 +1,50 @@
1
+ /** Проверяет собственное свойство без зависимости от ES2022 Object.hasOwn. / Checks an own property without the ES2022 Object.hasOwn API. */
2
+ export function hasOwn(object: object, key: PropertyKey): boolean {
3
+ return Object.prototype.hasOwnProperty.call(object, key)
4
+ }
5
+
6
+ /** Возвращает последний элемент без зависимости от ES2022 Array.at. / Returns the last item without the ES2022 Array.at API. */
7
+ export function lastItem<T>(items: readonly T[]): T | undefined {
8
+ return items.length > 0 ? items[items.length - 1] : undefined
9
+ }
10
+
11
+ /** Ошибка с ES2022-compatible cause, доступная в ES2020 runtime. / An ES2022-compatible error cause for ES2020 runtimes. */
12
+ export type ErrorWithCause<T extends Error = Error> = T & { cause: unknown }
13
+
14
+ /** Добавляет стандартный non-enumerable cause без требования ES2022 lib. / Adds the standard non-enumerable cause without requiring the ES2022 lib. */
15
+ export function withErrorCause<T extends Error>(error: T, cause: unknown): ErrorWithCause<T> {
16
+ Object.defineProperty(error, 'cause', {
17
+ configurable: true,
18
+ value: cause,
19
+ writable: true,
20
+ })
21
+ return error as ErrorWithCause<T>
22
+ }
23
+
24
+ interface AggregateErrorLike extends Error {
25
+ readonly errors: readonly unknown[]
26
+ }
27
+
28
+ interface AggregateErrorConstructorLike {
29
+ new (errors: Iterable<unknown>, message?: string): AggregateErrorLike
30
+ }
31
+
32
+ class Es2020AggregateError extends Error implements AggregateErrorLike {
33
+ readonly errors: readonly unknown[]
34
+
35
+ constructor(errors: readonly unknown[], message: string) {
36
+ super(message)
37
+ this.name = 'AggregateError'
38
+ this.errors = [...errors]
39
+ }
40
+ }
41
+
42
+ /** Создаёт native AggregateError при наличии и точный ES2020 fallback иначе. / Creates a native AggregateError when available and an exact ES2020 fallback otherwise. */
43
+ export function createAggregateError(errors: readonly unknown[], message: string): AggregateErrorLike {
44
+ const candidate: unknown = (globalThis as { AggregateError?: unknown }).AggregateError
45
+ if (typeof candidate === 'function') {
46
+ const AggregateErrorConstructor = candidate as AggregateErrorConstructorLike
47
+ return new AggregateErrorConstructor(errors, message)
48
+ }
49
+ return new Es2020AggregateError(errors, message)
50
+ }
@@ -1,4 +1,5 @@
1
1
  import { validateBoundedInteger, validateNonEmptyString } from './runtimeContract'
2
+ import { hasOwn, lastItem } from './es2020'
2
3
 
3
4
  export type InlineTokenEditorSegmentContract =
4
5
  | { readonly kind: 'text'; readonly text: string }
@@ -44,7 +45,7 @@ export function parseInlineTokenEditorSource(
44
45
  const parsed: InlineTokenEditorSegmentContract[] = []
45
46
  const tokenOccurrences = new Map<string, number>()
46
47
  for (let index = 0; index < segments.length; index += 1) {
47
- if (!Object.hasOwn(segments, index)) {
48
+ if (!hasOwn(segments, index)) {
48
49
  throw new TypeError(`SInlineTokenEditor: segments[${index}] отсутствует в sparse array. / SInlineTokenEditor: segments[${index}] is missing from a sparse array.`)
49
50
  }
50
51
  const record = assertPlainRecord(`SInlineTokenEditor.segments[${index}]`, segments[index])
@@ -54,7 +55,7 @@ export function parseInlineTokenEditorSource(
54
55
  throw new TypeError(`SInlineTokenEditor: segments[${index}].text должен быть непустой строкой. / SInlineTokenEditor: segments[${index}].text must be a non-empty string.`)
55
56
  }
56
57
  const text = record.text
57
- if (parsed.at(-1)?.kind === 'text') {
58
+ if (lastItem(parsed)?.kind === 'text') {
58
59
  throw new TypeError(`SInlineTokenEditor: segments[${index}] является смежным text segment; объедините его в источнике. / SInlineTokenEditor: segments[${index}] is an adjacent text segment; merge it at the source.`)
59
60
  }
60
61
  parsed.push({ kind: 'text', text })
@@ -122,7 +123,7 @@ export function composeInlineTokenSegments(
122
123
  for (const segment of segments) {
123
124
  if (segment.kind === 'text') {
124
125
  if (segment.text.length === 0) continue
125
- const previous = result.at(-1)
126
+ const previous = lastItem(result)
126
127
  if (previous?.kind === 'text') {
127
128
  result[result.length - 1] = { kind: 'text', text: previous.text + segment.text }
128
129
  } else {
@@ -19,7 +19,7 @@ let preservedCursor = ''
19
19
  let preservedUserSelect = ''
20
20
 
21
21
  const applyActiveInteraction = (): void => {
22
- const interaction = activeInteractions.at(-1)
22
+ const interaction = lastItem(activeInteractions)
23
23
  if (!lockedBody || !interaction) return
24
24
  lockedBody.style.cursor = interaction.cursor
25
25
  lockedBody.style.userSelect = 'none'
@@ -79,3 +79,4 @@ export function acquirePointerInteractionLease(
79
79
  },
80
80
  }
81
81
  }
82
+ import { lastItem } from './es2020'
@@ -2,8 +2,8 @@ import { validateExactString } from './runtimeContract'
2
2
 
3
3
  const SCROLLABLE_VIEWPORT_MAX_BLOCK_SIZES = ['none', 'sm', 'md', 'lg'] as const
4
4
  const EXPANDED_CONTENT_MAX_BLOCK_SIZES = ['sm', 'md', 'lg'] as const
5
- const TABLE_COLUMN_INLINE_SIZES = ['auto', 'xs', 'sm', 'md', 'lg', 'xl', 'fill'] as const
6
- const TABLE_COLUMN_MIN_INLINE_SIZES = ['xs', 'sm', 'md', 'lg', 'xl'] as const
5
+ const TABLE_COLUMN_INLINE_SIZES = ['auto', 'content', '2xs', 'xs', 'sm', 'md', 'lg', 'xl', 'fill'] as const
6
+ const TABLE_COLUMN_MIN_INLINE_SIZES = ['2xs', 'xs', 'sm', 'md', 'lg', 'xl'] as const
7
7
 
8
8
  /**
9
9
  * Максимальный block-size прокручиваемого viewport; none сохраняет естественный flow.
@@ -212,6 +212,7 @@
212
212
  --s-expanded-content-max-block-size-lg: 28rem;
213
213
  --s-table-inline-size-md: 32rem;
214
214
  --s-table-inline-size-lg: 42rem;
215
+ --s-table-column-inline-size-2xs: 3rem;
215
216
  --s-table-column-inline-size-xs: 6rem;
216
217
  --s-table-column-inline-size-sm: 9rem;
217
218
  --s-table-column-inline-size-md: 12rem;