@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.
- package/README.md +36 -0
- package/docs/public-api.md +8 -0
- package/package.json +1 -1
- package/src/components/layout/SAppShell.vue +3 -2
- package/src/components/shared/complex/STree.vue +29 -4
- package/src/components/shared/complex/treeAdapter.ts +2 -1
- package/src/components/shared/complex/types.ts +1 -12
- package/src/components/shared/containers/SLeftSidebar.vue +7 -2
- package/src/components/shared/containers/SPageHeader.css +5 -0
- package/src/components/shared/containers/SPageHeader.vue +1 -1
- package/src/components/shared/containers/SPopover.vue +2 -1
- package/src/components/shared/containers/SSidebarGroup.vue +10 -5
- package/src/components/shared/containers/sidebar.ts +2 -1
- package/src/components/shared/controls/SButton.vue +10 -5
- package/src/components/shared/controls/SCombobox.vue +47 -5
- package/src/components/shared/controls/SInteractiveSurface.vue +2 -1
- package/src/components/shared/controls/SLink.ts +1 -17
- package/src/components/shared/controls/SLink.vue +26 -2
- package/src/components/shared/data-display/SDocBlock.vue +10 -10
- package/src/components/shared/data-display/SLinkedSystemsList.vue +3 -3
- package/src/components/shared/data-display/STable.css +39 -0
- package/src/components/shared/data-display/STable.vue +454 -67
- package/src/components/shared/data-display/STooltip.vue +2 -1
- package/src/components/shared/data-display/SVirtualList.ts +1 -16
- package/src/components/shared/data-display/SVirtualList.vue +18 -2
- package/src/components/shared/data-display/json.ts +1 -1
- package/src/components/shared/data-display/table.ts +4 -29
- package/src/components/shared/database/SDataGrid.vue +29 -8
- package/src/components/shared/database/SSqlEditor.vue +3 -2
- package/src/components/shared/database/dataGrid.ts +1 -17
- package/src/components/shared/navigation/STabs.vue +2 -1
- package/src/composables/uiPreferencesReset.ts +2 -1
- package/src/composables/useClipboard.ts +2 -1
- package/src/composables/useSidebarPanelState.ts +3 -2
- package/src/internal/es2020.ts +50 -0
- package/src/internal/inlineTokenEditorContract.ts +4 -3
- package/src/internal/pointerInteractionLease.ts +2 -1
- package/src/internal/semanticSizing.ts +2 -2
- 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 (!
|
|
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.
|
|
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
|
-
|
|
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 (!
|
|
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 (
|
|
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 =
|
|
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 =
|
|
295
|
-
const hasLine =
|
|
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
|
|
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
|
|
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
|
|
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',
|
|
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
|
|
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 (
|
|
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 (!
|
|
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
|
|
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
|
|
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
|
|
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.
|
package/src/styles/tokens.css
CHANGED
|
@@ -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;
|