@pgcorp/ui-kit 0.1.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/LICENSE +21 -0
- package/README.md +157 -0
- package/docs/accessibility.md +27 -0
- package/docs/getting-started.md +80 -0
- package/docs/licensing.md +30 -0
- package/docs/public-api.md +39 -0
- package/docs/security.md +21 -0
- package/docs/theming.md +35 -0
- package/docs/versioning.md +23 -0
- package/index.ts +3 -0
- package/package.json +592 -0
- package/src/components/icons/_internal/generatedSputnigUiIconMarkup.ts +971 -0
- package/src/components/icons/sputnigUiIcons.ts +530 -0
- package/src/components/layout/SAppShell.vue +232 -0
- package/src/components/layout/SDockRegion.vue +200 -0
- package/src/components/layout/SResizeHandle.vue +251 -0
- package/src/components/layout/SSideMenuButton.vue +104 -0
- package/src/components/layout/SSideMenuRail.vue +47 -0
- package/src/components/layout/SStack.css +25 -0
- package/src/components/layout/SStack.vue +57 -0
- package/src/components/layout/SStatusBarShell.css +15 -0
- package/src/components/layout/SStatusBarShell.vue +26 -0
- package/src/components/layout/SWorkbenchLayout.vue +53 -0
- package/src/components/layout/SWorkbenchPanelShell.css +161 -0
- package/src/components/layout/SWorkbenchPanelShell.vue +286 -0
- package/src/components/layout/_internal/SAppBrand.css +45 -0
- package/src/components/layout/_internal/SAppBrand.vue +37 -0
- package/src/components/layout/_internal/SAppBrandContent.vue +30 -0
- package/src/components/layout/types.ts +28 -0
- package/src/components/shared/_internal/SDialogSurface.css +127 -0
- package/src/components/shared/_internal/SDialogSurface.vue +117 -0
- package/src/components/shared/_internal/SDisclosureSummary.css +92 -0
- package/src/components/shared/_internal/SDisclosureSummary.vue +121 -0
- package/src/components/shared/_internal/SListboxGroup.css +18 -0
- package/src/components/shared/_internal/SListboxGroup.vue +24 -0
- package/src/components/shared/_internal/SMenuItem.css +112 -0
- package/src/components/shared/_internal/SMenuItem.vue +213 -0
- package/src/components/shared/_internal/SMenuLayer.css +21 -0
- package/src/components/shared/_internal/SMenuLayer.vue +48 -0
- package/src/components/shared/_internal/SMenuSeparator.css +5 -0
- package/src/components/shared/_internal/SMenuSeparator.vue +13 -0
- package/src/components/shared/_internal/SToastItem.css +65 -0
- package/src/components/shared/_internal/SToastItem.vue +58 -0
- package/src/components/shared/_internal/STreeNode.vue +761 -0
- package/src/components/shared/_internal/dialogSurface.ts +105 -0
- package/src/components/shared/_internal/focusTrap.ts +40 -0
- package/src/components/shared/_internal/listboxContext.ts +16 -0
- package/src/components/shared/_internal/markerContract.ts +10 -0
- package/src/components/shared/_internal/menuContext.ts +23 -0
- package/src/components/shared/_internal/selectionContract.ts +239 -0
- package/src/components/shared/_internal/sidebarGroupContext.ts +31 -0
- package/src/components/shared/_internal/tabsContext.ts +57 -0
- package/src/components/shared/_internal/treeRuntime.ts +203 -0
- package/src/components/shared/_internal/useSelectionRoving.ts +162 -0
- package/src/components/shared/codeLanguages.ts +168 -0
- package/src/components/shared/complex/SKanbanCard.css +8 -0
- package/src/components/shared/complex/SKanbanCard.vue +76 -0
- package/src/components/shared/complex/SKanbanLane.vue +82 -0
- package/src/components/shared/complex/STree.css +24 -0
- package/src/components/shared/complex/STree.vue +288 -0
- package/src/components/shared/complex/treeAdapter.ts +210 -0
- package/src/components/shared/complex/types.ts +102 -0
- package/src/components/shared/containers/SCloudChrome.css +103 -0
- package/src/components/shared/containers/SCloudChrome.vue +87 -0
- package/src/components/shared/containers/SCloudChromeEmpty.css +10 -0
- package/src/components/shared/containers/SCloudChromeEmpty.vue +18 -0
- package/src/components/shared/containers/SCloudChromeRow.css +78 -0
- package/src/components/shared/containers/SCloudChromeRow.vue +108 -0
- package/src/components/shared/containers/SCollapsiblePanel.css +57 -0
- package/src/components/shared/containers/SCollapsiblePanel.vue +360 -0
- package/src/components/shared/containers/SDrawer.css +60 -0
- package/src/components/shared/containers/SDrawer.vue +86 -0
- package/src/components/shared/containers/SFormGrid.css +26 -0
- package/src/components/shared/containers/SFormGrid.vue +46 -0
- package/src/components/shared/containers/SFormGridItem.css +18 -0
- package/src/components/shared/containers/SFormGridItem.vue +29 -0
- package/src/components/shared/containers/SLeftSidebar.vue +74 -0
- package/src/components/shared/containers/SModal.css +27 -0
- package/src/components/shared/containers/SModal.vue +105 -0
- package/src/components/shared/containers/SPageHeader.css +66 -0
- package/src/components/shared/containers/SPageHeader.vue +45 -0
- package/src/components/shared/containers/SPanel.css +134 -0
- package/src/components/shared/containers/SPanel.vue +169 -0
- package/src/components/shared/containers/SPopover.css +35 -0
- package/src/components/shared/containers/SPopover.vue +551 -0
- package/src/components/shared/containers/SSidebarGroup.css +12 -0
- package/src/components/shared/containers/SSidebarGroup.vue +443 -0
- package/src/components/shared/containers/SSidebarSection.css +41 -0
- package/src/components/shared/containers/SSidebarSection.vue +268 -0
- package/src/components/shared/containers/disclosureSummary.ts +48 -0
- package/src/components/shared/containers/popover.ts +72 -0
- package/src/components/shared/containers/sidebar.ts +206 -0
- package/src/components/shared/controls/SButton.css +274 -0
- package/src/components/shared/controls/SButton.vue +211 -0
- package/src/components/shared/controls/SCheckbox.css +65 -0
- package/src/components/shared/controls/SCheckbox.vue +130 -0
- package/src/components/shared/controls/SCheckboxGroup.vue +134 -0
- package/src/components/shared/controls/SChoiceCards.css +29 -0
- package/src/components/shared/controls/SChoiceCards.vue +145 -0
- package/src/components/shared/controls/SColorSelect.vue +146 -0
- package/src/components/shared/controls/SComboboxTrigger.css +35 -0
- package/src/components/shared/controls/SComboboxTrigger.vue +122 -0
- package/src/components/shared/controls/SContextMenu.css +11 -0
- package/src/components/shared/controls/SContextMenu.vue +186 -0
- package/src/components/shared/controls/SContextToggleButton.vue +64 -0
- package/src/components/shared/controls/SDragHandle.vue +100 -0
- package/src/components/shared/controls/SDropdownMenu.css +5 -0
- package/src/components/shared/controls/SDropdownMenu.vue +188 -0
- package/src/components/shared/controls/SField.css +79 -0
- package/src/components/shared/controls/SField.vue +89 -0
- package/src/components/shared/controls/SFieldGroup.css +58 -0
- package/src/components/shared/controls/SFieldGroup.vue +117 -0
- package/src/components/shared/controls/SFieldLabel.css +12 -0
- package/src/components/shared/controls/SFieldLabel.vue +51 -0
- package/src/components/shared/controls/SInlineTokenEditor.css +61 -0
- package/src/components/shared/controls/SInlineTokenEditor.vue +1081 -0
- package/src/components/shared/controls/SInputText.css +66 -0
- package/src/components/shared/controls/SInputText.vue +321 -0
- package/src/components/shared/controls/SInteractiveSurface.css +72 -0
- package/src/components/shared/controls/SInteractiveSurface.vue +326 -0
- package/src/components/shared/controls/SLink.css +114 -0
- package/src/components/shared/controls/SLink.ts +85 -0
- package/src/components/shared/controls/SLink.vue +114 -0
- package/src/components/shared/controls/SListbox.css +31 -0
- package/src/components/shared/controls/SListbox.vue +388 -0
- package/src/components/shared/controls/SListboxOption.css +63 -0
- package/src/components/shared/controls/SListboxOption.vue +85 -0
- package/src/components/shared/controls/SMarker.css +19 -0
- package/src/components/shared/controls/SMarker.vue +33 -0
- package/src/components/shared/controls/SMenuSurface.css +20 -0
- package/src/components/shared/controls/SMenuSurface.vue +333 -0
- package/src/components/shared/controls/SSegmentedControl.css +38 -0
- package/src/components/shared/controls/SSegmentedControl.vue +134 -0
- package/src/components/shared/controls/SSelect.css +12 -0
- package/src/components/shared/controls/SSelect.vue +338 -0
- package/src/components/shared/controls/STextarea.css +31 -0
- package/src/components/shared/controls/STextarea.vue +316 -0
- package/src/components/shared/controls/_internal/SInlineTokenSurface.vue +224 -0
- package/src/components/shared/controls/menu.ts +178 -0
- package/src/components/shared/data-display/SActionCard.css +6 -0
- package/src/components/shared/data-display/SActionCard.vue +49 -0
- package/src/components/shared/data-display/SActionList.css +15 -0
- package/src/components/shared/data-display/SActionList.vue +34 -0
- package/src/components/shared/data-display/SActionListItem.css +114 -0
- package/src/components/shared/data-display/SActionListItem.vue +230 -0
- package/src/components/shared/data-display/SBadge.css +73 -0
- package/src/components/shared/data-display/SBadge.vue +52 -0
- package/src/components/shared/data-display/SCodeBlock.css +56 -0
- package/src/components/shared/data-display/SCodeBlock.vue +188 -0
- package/src/components/shared/data-display/SCodeEditor.css +178 -0
- package/src/components/shared/data-display/SCodeEditor.vue +274 -0
- package/src/components/shared/data-display/SCodeSearchPanel.vue +327 -0
- package/src/components/shared/data-display/SCopyField.vue +77 -0
- package/src/components/shared/data-display/SDocBlock.css +136 -0
- package/src/components/shared/data-display/SDocBlock.vue +358 -0
- package/src/components/shared/data-display/SEmptyState.css +42 -0
- package/src/components/shared/data-display/SEmptyState.vue +79 -0
- package/src/components/shared/data-display/SExpandableText.vue +314 -0
- package/src/components/shared/data-display/SJsonTree.css +6 -0
- package/src/components/shared/data-display/SJsonTree.vue +111 -0
- package/src/components/shared/data-display/SKeyValueGrid.css +37 -0
- package/src/components/shared/data-display/SKeyValueGrid.vue +277 -0
- package/src/components/shared/data-display/SLinkedSystemsList.vue +270 -0
- package/src/components/shared/data-display/SMessage.css +88 -0
- package/src/components/shared/data-display/SMessage.vue +152 -0
- package/src/components/shared/data-display/SMetricCard.css +32 -0
- package/src/components/shared/data-display/SMetricCard.vue +117 -0
- package/src/components/shared/data-display/SProgressBar.css +84 -0
- package/src/components/shared/data-display/SProgressBar.vue +114 -0
- package/src/components/shared/data-display/SProgressIndicator.css +59 -0
- package/src/components/shared/data-display/SProgressIndicator.vue +104 -0
- package/src/components/shared/data-display/SSectionHeader.css +84 -0
- package/src/components/shared/data-display/SSectionHeader.vue +58 -0
- package/src/components/shared/data-display/SStatus.css +90 -0
- package/src/components/shared/data-display/SStatus.vue +79 -0
- package/src/components/shared/data-display/STable.css +83 -0
- package/src/components/shared/data-display/STable.vue +339 -0
- package/src/components/shared/data-display/SToastContainer.css +53 -0
- package/src/components/shared/data-display/SToastContainer.vue +31 -0
- package/src/components/shared/data-display/STooltip.css +114 -0
- package/src/components/shared/data-display/STooltip.vue +550 -0
- package/src/components/shared/data-display/STooltipAnchor.css +21 -0
- package/src/components/shared/data-display/STooltipAnchor.vue +119 -0
- package/src/components/shared/data-display/SVirtualList.css +24 -0
- package/src/components/shared/data-display/SVirtualList.ts +19 -0
- package/src/components/shared/data-display/SVirtualList.vue +361 -0
- package/src/components/shared/data-display/json.ts +136 -0
- package/src/components/shared/data-display/table.ts +76 -0
- package/src/components/shared/database/SDataGrid.css +3 -0
- package/src/components/shared/database/SDataGrid.vue +464 -0
- package/src/components/shared/database/SSqlEditor.vue +363 -0
- package/src/components/shared/database/dataGrid.ts +61 -0
- package/src/components/shared/feedback/SAsyncState.css +26 -0
- package/src/components/shared/feedback/SAsyncState.vue +190 -0
- package/src/components/shared/graph/SGraphViewport.ts +1 -0
- package/src/components/shared/graph/SGraphViewport.vue +858 -0
- package/src/components/shared/graph/_internal/graphViewportContract.ts +362 -0
- package/src/components/shared/markdown.ts +169 -0
- package/src/components/shared/navigation/SBottomNav.vue +692 -0
- package/src/components/shared/navigation/SBreadcrumbs.css +26 -0
- package/src/components/shared/navigation/SBreadcrumbs.vue +155 -0
- package/src/components/shared/navigation/SCatalogNavigator.css +100 -0
- package/src/components/shared/navigation/SCatalogNavigator.vue +308 -0
- package/src/components/shared/navigation/STab.vue +363 -0
- package/src/components/shared/navigation/STabList.vue +442 -0
- package/src/components/shared/navigation/STabPanel.vue +165 -0
- package/src/components/shared/navigation/STabPanels.vue +33 -0
- package/src/components/shared/navigation/STabs.vue +617 -0
- package/src/components/shared/navigation/SWizardSteps.css +69 -0
- package/src/components/shared/navigation/SWizardSteps.vue +171 -0
- package/src/components/shared/navigation/_internal/SCatalogNavigatorGroup.vue +63 -0
- package/src/components/shared/persona/SPersonaProjectPicker.css +36 -0
- package/src/components/shared/persona/SPersonaProjectPicker.vue +361 -0
- package/src/components/shared/persona/SPersonaRunWorkbench.css +109 -0
- package/src/components/shared/persona/SPersonaRunWorkbench.vue +549 -0
- package/src/components/shared/persona/_internal/SPersonaRunConsole.css +40 -0
- package/src/components/shared/persona/_internal/SPersonaRunConsole.vue +298 -0
- package/src/components/shared/persona/_internal/inventory.ts +110 -0
- package/src/components/shared/persona/_internal/labels.ts +79 -0
- package/src/components/shared/persona/presentation.ts +371 -0
- package/src/components/shared/persona/types.ts +256 -0
- package/src/composables/uiPreferencesReset.ts +52 -0
- package/src/composables/useClipboard.ts +37 -0
- package/src/composables/useDialogOverlay.ts +85 -0
- package/src/composables/useDisclosure.ts +40 -0
- package/src/composables/useFloatingPosition.ts +245 -0
- package/src/composables/useNotifier.ts +26 -0
- package/src/composables/useSidebarPanelState.ts +610 -0
- package/src/internal/codeEditorContract.ts +110 -0
- package/src/internal/codeEditorExtensions.ts +27 -0
- package/src/internal/feedbackPresentation.ts +136 -0
- package/src/internal/fieldSurface.ts +59 -0
- package/src/internal/focusNavigation.ts +69 -0
- package/src/internal/inlineTokenEditorContract.ts +136 -0
- package/src/internal/interactiveElement.ts +33 -0
- package/src/internal/layerStack.ts +202 -0
- package/src/internal/linkTarget.ts +256 -0
- package/src/internal/ownedAttrs.ts +464 -0
- package/src/internal/passiveContentContract.ts +129 -0
- package/src/internal/pointerInteractionLease.ts +81 -0
- package/src/internal/runtimeContract.ts +194 -0
- package/src/internal/runtimeTheme.ts +159 -0
- package/src/internal/semanticSizing.ts +60 -0
- package/src/internal/useMenuLayer.ts +129 -0
- package/src/stores/useNotifierStore.ts +69 -0
- package/src/styles/reference.css +4 -0
- package/src/styles/style.css +151 -0
- package/src/styles/tokens.css +836 -0
- package/src/theme.ts +449 -0
- package/src/types/highlight-js-lib.d.ts +76 -0
- package/src/variants.ts +15 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Стили компонента SInputText.
|
|
3
|
+
*/
|
|
4
|
+
@reference "../../../styles/reference.css";
|
|
5
|
+
|
|
6
|
+
.s-input-text-wrapper {
|
|
7
|
+
@apply w-full min-w-0 box-border relative;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.s-input-text {
|
|
11
|
+
@apply block w-full min-w-0 box-border;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.s-input-text[data-size='sm'] { @apply px-2.5 py-1.5 text-xs; }
|
|
15
|
+
.s-input-text[data-size='md'] { @apply px-3 py-2 text-sm; }
|
|
16
|
+
.s-input-text[data-size='lg'] { @apply px-4 py-2.5 text-lg; }
|
|
17
|
+
|
|
18
|
+
.s-input-text--monospace {
|
|
19
|
+
@apply font-mono;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.s-input-text[data-appearance='grid-cell'] {
|
|
23
|
+
@apply h-8 rounded-none border-transparent bg-transparent px-2 py-1 text-sm shadow-none;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.s-input-text[data-appearance='workbench-title'] {
|
|
27
|
+
@apply w-full border-transparent bg-transparent text-xl font-bold shadow-none ring-0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.s-input-text[data-appearance='workbench-title']:focus-visible {
|
|
31
|
+
@apply bg-surface-0 dark:bg-surface-900;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.s-input-control {
|
|
35
|
+
@apply relative;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.s-input-text-wrapper[data-trailing='icon'] .s-input-text,
|
|
39
|
+
.s-input-text-wrapper[data-trailing='text'] .s-input-text {
|
|
40
|
+
@apply pr-10;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.s-input-text-wrapper[data-trailing='action'] .s-input-text {
|
|
44
|
+
@apply pr-12;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.s-input-trailing {
|
|
48
|
+
@apply absolute right-2 flex items-center justify-center;
|
|
49
|
+
top: 50%;
|
|
50
|
+
transform: translateY(-50%);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.s-input-trailing__passive {
|
|
54
|
+
@apply inline-flex items-center justify-center text-surface-400 dark:text-surface-500;
|
|
55
|
+
width: var(--s-icon-size-sm);
|
|
56
|
+
height: var(--s-icon-size-sm);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.s-input-trailing__icon {
|
|
60
|
+
@apply h-full w-full;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.s-input-trailing__text {
|
|
64
|
+
@apply inline-flex min-w-5 items-center justify-center rounded border border-surface-200 bg-surface-50 px-1 text-xs font-semibold text-surface-500;
|
|
65
|
+
@apply dark:border-surface-700 dark:bg-surface-800 dark:text-surface-400;
|
|
66
|
+
}
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="s-input-text-wrapper"
|
|
4
|
+
:data-input-family="inputTypeContract.family"
|
|
5
|
+
:data-trailing="trailingKind"
|
|
6
|
+
:data-with-label="Boolean(label) || undefined"
|
|
7
|
+
>
|
|
8
|
+
<SField
|
|
9
|
+
:control-id="inputId"
|
|
10
|
+
:label="label"
|
|
11
|
+
:required="required"
|
|
12
|
+
:help-text="helpText"
|
|
13
|
+
:error-message="errorMessage"
|
|
14
|
+
:invalid="invalid"
|
|
15
|
+
:disabled="disabled"
|
|
16
|
+
:readonly="readonly"
|
|
17
|
+
>
|
|
18
|
+
<template #default="field">
|
|
19
|
+
<div class="s-input-control">
|
|
20
|
+
<input
|
|
21
|
+
v-bind="{ ...ownedAttrs.bindings(), ...fieldSurfaceBindings(field) }"
|
|
22
|
+
:id="field.controlId"
|
|
23
|
+
:name="name"
|
|
24
|
+
ref="inputRef"
|
|
25
|
+
:type="inputTypeContract.type"
|
|
26
|
+
class="s-input-text"
|
|
27
|
+
:class="{ 's-input-text--monospace': monospace }"
|
|
28
|
+
:data-size="validatedSize"
|
|
29
|
+
:data-appearance="validatedAppearance"
|
|
30
|
+
:value="modelValue"
|
|
31
|
+
:placeholder="placeholder"
|
|
32
|
+
:autocomplete="autocomplete"
|
|
33
|
+
:autocapitalize="autocapitalize"
|
|
34
|
+
:autocorrect="autocorrect"
|
|
35
|
+
:spellcheck="spellcheck"
|
|
36
|
+
:disabled="field.disabled"
|
|
37
|
+
:readonly="field.readonly"
|
|
38
|
+
:required="field.required"
|
|
39
|
+
:aria-invalid="field.invalid || undefined"
|
|
40
|
+
:aria-describedby="mergeIdReferences(ownedAttrs.string('aria-describedby'), field.describedBy)"
|
|
41
|
+
@input="onInput"
|
|
42
|
+
@change="onChange"
|
|
43
|
+
/>
|
|
44
|
+
<div v-if="trailingKind" class="s-input-trailing" :data-kind="trailingKind">
|
|
45
|
+
<span v-if="trailingIcon" class="s-input-trailing__passive" aria-hidden="true">
|
|
46
|
+
<component :is="trailingIcon" class="s-input-trailing__icon" />
|
|
47
|
+
</span>
|
|
48
|
+
<span v-else-if="trailingText !== undefined" class="s-input-trailing__text" aria-hidden="true">
|
|
49
|
+
{{ trailingText }}
|
|
50
|
+
</span>
|
|
51
|
+
<SButton
|
|
52
|
+
v-else-if="trailingAction"
|
|
53
|
+
label=""
|
|
54
|
+
size="sm"
|
|
55
|
+
appearance="ghost"
|
|
56
|
+
shape="square"
|
|
57
|
+
icon-only
|
|
58
|
+
:severity="trailingAction.severity ?? 'secondary'"
|
|
59
|
+
:leading-icon="trailingAction.icon"
|
|
60
|
+
:loading="trailingAction.loading"
|
|
61
|
+
:disabled="field.disabled || trailingAction.disabled"
|
|
62
|
+
:title="trailingAction.title ?? trailingAction.label"
|
|
63
|
+
:aria-label="trailingAction.label"
|
|
64
|
+
@click="emit('trailing-action', $event)"
|
|
65
|
+
/>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</template>
|
|
69
|
+
</SField>
|
|
70
|
+
</div>
|
|
71
|
+
</template>
|
|
72
|
+
|
|
73
|
+
<script setup lang="ts">
|
|
74
|
+
import { computed, ref, useId, watchEffect, type Component } from 'vue'
|
|
75
|
+
import { mergeIdReferences, useOwnedAttrs } from '../../../internal/ownedAttrs'
|
|
76
|
+
import { useInteractiveLeafRegistration } from '../../../internal/passiveContentContract'
|
|
77
|
+
import { resolveFieldSurfaceBindings, type FieldSurfaceBindings } from '../../../internal/fieldSurface'
|
|
78
|
+
import { resolveOptionalDomId, validateExactString } from '../../../internal/runtimeContract'
|
|
79
|
+
import SButton, { type SButtonSeverity } from './SButton.vue'
|
|
80
|
+
import SField from './SField.vue'
|
|
81
|
+
|
|
82
|
+
defineOptions({
|
|
83
|
+
inheritAttrs: false,
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
/** Текстовые native input types с text-editing semantics. / Textual native input types with text-editing semantics. */
|
|
87
|
+
export type SInputTextTextualType = 'text' | 'search' | 'password' | 'email' | 'tel' | 'url'
|
|
88
|
+
/** Числовой native input type; model events остаются строками. / Numeric native input type; model events remain strings. */
|
|
89
|
+
export type SInputTextNumericType = 'number'
|
|
90
|
+
/** Временные native types со строковым browser value. / Temporal native types with a browser-formatted string value. */
|
|
91
|
+
export type SInputTextTemporalType = 'date' | 'datetime-local' | 'month' | 'time' | 'week'
|
|
92
|
+
/** Конечная native type surface владельца SInputText. / Finite native type surface owned by SInputText. */
|
|
93
|
+
export type SInputTextType = SInputTextTextualType | SInputTextNumericType | SInputTextTemporalType
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Дискриминированный native input contract без file/range/choice/button semantics.
|
|
97
|
+
* Discriminated native input contract without file/range/choice/button semantics.
|
|
98
|
+
*/
|
|
99
|
+
export type SInputTextTypeContract =
|
|
100
|
+
| { family: 'textual'; type: SInputTextTextualType }
|
|
101
|
+
| { family: 'numeric'; type: SInputTextNumericType }
|
|
102
|
+
| { family: 'temporal'; type: SInputTextTemporalType }
|
|
103
|
+
|
|
104
|
+
const INPUT_TYPES = [
|
|
105
|
+
'text',
|
|
106
|
+
'search',
|
|
107
|
+
'password',
|
|
108
|
+
'email',
|
|
109
|
+
'tel',
|
|
110
|
+
'url',
|
|
111
|
+
'number',
|
|
112
|
+
'date',
|
|
113
|
+
'datetime-local',
|
|
114
|
+
'month',
|
|
115
|
+
'time',
|
|
116
|
+
'week',
|
|
117
|
+
] as const satisfies readonly SInputTextType[]
|
|
118
|
+
|
|
119
|
+
const INPUT_TYPE_CONTRACTS = {
|
|
120
|
+
text: { family: 'textual', type: 'text' },
|
|
121
|
+
search: { family: 'textual', type: 'search' },
|
|
122
|
+
password: { family: 'textual', type: 'password' },
|
|
123
|
+
email: { family: 'textual', type: 'email' },
|
|
124
|
+
tel: { family: 'textual', type: 'tel' },
|
|
125
|
+
url: { family: 'textual', type: 'url' },
|
|
126
|
+
number: { family: 'numeric', type: 'number' },
|
|
127
|
+
date: { family: 'temporal', type: 'date' },
|
|
128
|
+
'datetime-local': { family: 'temporal', type: 'datetime-local' },
|
|
129
|
+
month: { family: 'temporal', type: 'month' },
|
|
130
|
+
time: { family: 'temporal', type: 'time' },
|
|
131
|
+
week: { family: 'temporal', type: 'week' },
|
|
132
|
+
} as const satisfies Record<SInputTextType, SInputTextTypeContract>
|
|
133
|
+
|
|
134
|
+
export interface Props {
|
|
135
|
+
modelValue: string | null
|
|
136
|
+
/** Стандартные Vue v-model modifiers для строкового input contract. / Standard Vue v-model modifiers for the string input contract. */
|
|
137
|
+
modelModifiers?: SInputTextModelModifiers
|
|
138
|
+
label?: string
|
|
139
|
+
placeholder?: string
|
|
140
|
+
disabled?: boolean
|
|
141
|
+
readonly?: boolean
|
|
142
|
+
required?: boolean
|
|
143
|
+
monospace?: boolean
|
|
144
|
+
size?: 'sm' | 'md' | 'lg'
|
|
145
|
+
appearance?: 'field' | 'grid-cell' | 'workbench-title'
|
|
146
|
+
invalid?: boolean
|
|
147
|
+
errorMessage?: string
|
|
148
|
+
helpText?: string
|
|
149
|
+
id?: string
|
|
150
|
+
name?: string
|
|
151
|
+
type?: SInputTextType
|
|
152
|
+
trailingIcon?: Component
|
|
153
|
+
trailingText?: string
|
|
154
|
+
trailingAction?: SInputTextTrailingAction
|
|
155
|
+
autocomplete?: string
|
|
156
|
+
autocapitalize?: 'none' | 'off' | 'sentences' | 'words' | 'characters'
|
|
157
|
+
autocorrect?: 'on' | 'off'
|
|
158
|
+
spellcheck?: boolean
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export interface SInputTextModelModifiers {
|
|
162
|
+
trim?: boolean
|
|
163
|
+
lazy?: boolean
|
|
164
|
+
number?: boolean
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Типизированное действие в trailing-region поля; geometry принадлежит SInputText/SButton.
|
|
169
|
+
* Typed trailing action whose geometry is owned by SInputText/SButton.
|
|
170
|
+
*/
|
|
171
|
+
export interface SInputTextTrailingAction {
|
|
172
|
+
label: string
|
|
173
|
+
icon: Component
|
|
174
|
+
title?: string
|
|
175
|
+
severity?: SButtonSeverity
|
|
176
|
+
disabled?: boolean
|
|
177
|
+
loading?: boolean
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
181
|
+
modelValue: '',
|
|
182
|
+
modelModifiers: () => ({}),
|
|
183
|
+
label: undefined,
|
|
184
|
+
placeholder: undefined,
|
|
185
|
+
disabled: false,
|
|
186
|
+
readonly: false,
|
|
187
|
+
required: false,
|
|
188
|
+
monospace: false,
|
|
189
|
+
size: 'md',
|
|
190
|
+
appearance: 'field',
|
|
191
|
+
invalid: false,
|
|
192
|
+
errorMessage: undefined,
|
|
193
|
+
helpText: undefined,
|
|
194
|
+
id: undefined,
|
|
195
|
+
name: undefined,
|
|
196
|
+
type: 'text',
|
|
197
|
+
trailingIcon: undefined,
|
|
198
|
+
trailingText: undefined,
|
|
199
|
+
trailingAction: undefined,
|
|
200
|
+
autocomplete: undefined,
|
|
201
|
+
autocapitalize: undefined,
|
|
202
|
+
autocorrect: undefined,
|
|
203
|
+
spellcheck: undefined,
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
const emit = defineEmits<{
|
|
207
|
+
'update:modelValue': [value: string]
|
|
208
|
+
'trailing-action': [event: MouseEvent]
|
|
209
|
+
}>()
|
|
210
|
+
const ownedAttrs = useOwnedAttrs({ component: 'SInputText', owner: 'native input' })
|
|
211
|
+
useInteractiveLeafRegistration({ owner: 'SInputText' })
|
|
212
|
+
|
|
213
|
+
const inputRef = ref<HTMLInputElement | null>(null)
|
|
214
|
+
defineExpose({
|
|
215
|
+
focus: () => inputRef.value?.focus(),
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
const generatedInputId = useId()
|
|
219
|
+
const inputId = computed(() => resolveOptionalDomId(
|
|
220
|
+
'SInputText',
|
|
221
|
+
'id',
|
|
222
|
+
props.id,
|
|
223
|
+
`s-input-text-${generatedInputId}`,
|
|
224
|
+
))
|
|
225
|
+
const inputTypeContract = computed<SInputTextTypeContract>(() => {
|
|
226
|
+
const type = validateExactString('SInputText', 'type', props.type, INPUT_TYPES)
|
|
227
|
+
return INPUT_TYPE_CONTRACTS[type]
|
|
228
|
+
})
|
|
229
|
+
const validatedSize = computed(() => validateExactString(
|
|
230
|
+
'SInputText', 'size', props.size, ['sm', 'md', 'lg'] as const,
|
|
231
|
+
))
|
|
232
|
+
const validatedAppearance = computed(() => validateExactString(
|
|
233
|
+
'SInputText',
|
|
234
|
+
'appearance',
|
|
235
|
+
props.appearance,
|
|
236
|
+
['field', 'grid-cell', 'workbench-title'] as const,
|
|
237
|
+
))
|
|
238
|
+
const validatedModelModifiers = computed(() => {
|
|
239
|
+
const candidate: unknown = props.modelModifiers
|
|
240
|
+
if (typeof candidate !== 'object' || candidate === null || Array.isArray(candidate)) {
|
|
241
|
+
throw new TypeError('SInputText: modelModifiers должен быть object. / SInputText: modelModifiers must be an object.')
|
|
242
|
+
}
|
|
243
|
+
const modifiers = candidate as Record<string, unknown>
|
|
244
|
+
const allowed = new Set(['trim', 'lazy', 'number'])
|
|
245
|
+
const unknown = Object.keys(modifiers).filter((key) => !allowed.has(key))
|
|
246
|
+
if (unknown.length > 0) {
|
|
247
|
+
throw new Error(`SInputText: modelModifiers содержит неизвестные поля ${unknown.join(', ')}. / SInputText: modelModifiers contains unknown fields ${unknown.join(', ')}.`)
|
|
248
|
+
}
|
|
249
|
+
for (const key of allowed) {
|
|
250
|
+
if (modifiers[key] !== undefined && typeof modifiers[key] !== 'boolean') {
|
|
251
|
+
throw new TypeError(`SInputText: modelModifiers.${key} должен быть boolean. / SInputText: modelModifiers.${key} must be boolean.`)
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (modifiers.number === true) {
|
|
255
|
+
throw new Error(
|
|
256
|
+
'SInputText: modifier number несовместим со строковым modelValue; используйте числовой UI-kit control с числовой моделью. '
|
|
257
|
+
+ '/ SInputText: the number modifier is incompatible with string modelValue; use a numeric UI-kit control with a numeric model.',
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
trim: modifiers.trim === true,
|
|
262
|
+
lazy: modifiers.lazy === true,
|
|
263
|
+
}
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
function fieldSurfaceBindings(field: {
|
|
267
|
+
invalid: boolean
|
|
268
|
+
disabled: boolean
|
|
269
|
+
readonly: boolean
|
|
270
|
+
}): FieldSurfaceBindings {
|
|
271
|
+
return resolveFieldSurfaceBindings('SInputText', {
|
|
272
|
+
kind: 'text-input',
|
|
273
|
+
appearance: validatedAppearance.value === 'field' ? 'field' : 'embedded',
|
|
274
|
+
invalid: field.invalid,
|
|
275
|
+
disabled: field.disabled,
|
|
276
|
+
readonly: field.readonly,
|
|
277
|
+
})
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const trailingKind = computed<'icon' | 'text' | 'action' | undefined>(() => {
|
|
281
|
+
if (props.trailingIcon) return 'icon'
|
|
282
|
+
if (props.trailingText !== undefined) return 'text'
|
|
283
|
+
if (props.trailingAction) return 'action'
|
|
284
|
+
return undefined
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
watchEffect(() => {
|
|
288
|
+
void validatedModelModifiers.value
|
|
289
|
+
const trailingContracts = [props.trailingIcon, props.trailingText, props.trailingAction]
|
|
290
|
+
.filter((value) => value !== undefined)
|
|
291
|
+
if (trailingContracts.length > 1) {
|
|
292
|
+
throw new Error('SInputText: trailingIcon, trailingText и trailingAction взаимоисключающие')
|
|
293
|
+
}
|
|
294
|
+
if (props.trailingText !== undefined && props.trailingText.length === 0) {
|
|
295
|
+
throw new Error('SInputText: trailingText не может быть пустым')
|
|
296
|
+
}
|
|
297
|
+
if (props.trailingAction && props.trailingAction.label.trim().length === 0) {
|
|
298
|
+
throw new Error('SInputText: trailingAction.label обязателен')
|
|
299
|
+
}
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
function emitInputValue(event: Event): void {
|
|
303
|
+
const target = event.target
|
|
304
|
+
if (!(target instanceof HTMLInputElement)) {
|
|
305
|
+
throw new TypeError('SInputText: input/change event target должен быть HTMLInputElement')
|
|
306
|
+
}
|
|
307
|
+
const value = validatedModelModifiers.value.trim ? target.value.trim() : target.value
|
|
308
|
+
emit('update:modelValue', value)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function onInput(event: Event): void {
|
|
312
|
+
if (!validatedModelModifiers.value.lazy) emitInputValue(event)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function onChange(event: Event): void {
|
|
316
|
+
if (validatedModelModifiers.value.lazy) emitInputValue(event)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
</script>
|
|
320
|
+
|
|
321
|
+
<style lang="postcss" src="./SInputText.css" scoped></style>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
@reference "../../../styles/reference.css";
|
|
2
|
+
|
|
3
|
+
.s-interactive-surface {
|
|
4
|
+
@apply relative;
|
|
5
|
+
@apply flex min-h-0 min-w-0 overflow-hidden border border-surface-200 bg-surface-50 text-surface-700;
|
|
6
|
+
@apply dark:border-surface-700 dark:bg-surface-800/70 dark:text-surface-200;
|
|
7
|
+
font: inherit;
|
|
8
|
+
text-decoration: none;
|
|
9
|
+
appearance: none;
|
|
10
|
+
transition:
|
|
11
|
+
background-color var(--s-motion-standard) ease,
|
|
12
|
+
border-color var(--s-motion-standard) ease,
|
|
13
|
+
box-shadow var(--s-motion-standard) ease,
|
|
14
|
+
color var(--s-motion-standard) ease;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.s-interactive-surface__activation {
|
|
18
|
+
@apply absolute inset-0 z-10 block h-full w-full cursor-pointer border-0 bg-transparent p-0;
|
|
19
|
+
border-radius: inherit;
|
|
20
|
+
color: inherit;
|
|
21
|
+
font: inherit;
|
|
22
|
+
appearance: none;
|
|
23
|
+
text-decoration: none;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.s-interactive-surface__activation:disabled,
|
|
27
|
+
.s-interactive-surface[data-disabled='true'] .s-interactive-surface__activation {
|
|
28
|
+
@apply cursor-not-allowed;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.s-interactive-surface__accessible-label { @apply sr-only; }
|
|
32
|
+
|
|
33
|
+
.s-interactive-surface__content {
|
|
34
|
+
@apply relative flex min-h-0 min-w-0 w-full flex-1;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.s-interactive-surface:not([data-mode='static']) .s-interactive-surface__content {
|
|
38
|
+
pointer-events: none;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.s-interactive-surface[data-fill='container'] { @apply h-full w-full; }
|
|
42
|
+
.s-interactive-surface[data-density='compact'] { @apply text-sm; }
|
|
43
|
+
.s-interactive-surface[data-density='comfortable'] { @apply text-base; }
|
|
44
|
+
.s-interactive-surface[data-padding='none'] { @apply p-0; }
|
|
45
|
+
.s-interactive-surface[data-padding='sm'] { @apply p-3; }
|
|
46
|
+
.s-interactive-surface[data-padding='md'] { @apply p-4; }
|
|
47
|
+
.s-interactive-surface[data-radius='md'] { @apply rounded-md; }
|
|
48
|
+
.s-interactive-surface[data-radius='lg'] { @apply rounded-lg; }
|
|
49
|
+
.s-interactive-surface[data-radius='xl'] { @apply rounded-xl; }
|
|
50
|
+
.s-interactive-surface[data-tone='primary'] { @apply border-primary-200 bg-primary-500/5 dark:border-primary-500/30 dark:bg-primary-500/10; }
|
|
51
|
+
.s-interactive-surface[data-tone='success'] { @apply border-success-200 bg-success-50 dark:border-success-800 dark:bg-success-950; }
|
|
52
|
+
.s-interactive-surface[data-tone='danger'] { @apply border-danger-200 bg-danger-50 dark:border-danger-800 dark:bg-danger-950; }
|
|
53
|
+
.s-interactive-surface[data-selected='true'] { @apply border-primary-400 bg-primary-50 dark:border-primary-600 dark:bg-primary-950; }
|
|
54
|
+
.s-interactive-surface[data-pressed='true'] { @apply border-primary-500 bg-primary-100 dark:border-primary-500 dark:bg-primary-950/70; }
|
|
55
|
+
.s-interactive-surface[data-disabled='true'] { @apply cursor-not-allowed opacity-60; }
|
|
56
|
+
.s-interactive-surface[data-content-align='start'] { @apply justify-start text-left; }
|
|
57
|
+
.s-interactive-surface[data-content-align='center'] { @apply justify-center text-center; }
|
|
58
|
+
|
|
59
|
+
.s-interactive-surface[data-mode='action']:not([data-disabled='true']),
|
|
60
|
+
.s-interactive-surface[data-mode='link']:not([data-disabled='true']) {
|
|
61
|
+
cursor: pointer;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.s-interactive-surface[data-mode='action']:not([data-disabled='true']):hover,
|
|
65
|
+
.s-interactive-surface[data-mode='link']:not([data-disabled='true']):hover {
|
|
66
|
+
@apply border-surface-300 text-surface-900 no-underline dark:border-surface-600 dark:text-surface-50;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.s-interactive-surface:has(.s-interactive-surface__activation:focus-visible) {
|
|
70
|
+
outline: var(--s-focus-ring-width) solid var(--s-focus-ring-color);
|
|
71
|
+
outline-offset: var(--s-focus-ring-offset);
|
|
72
|
+
}
|