@skewedaspect/sleekspace-ui 0.9.0 → 0.10.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/dist/components/Accordion/context.d.ts +4 -0
- package/dist/components/Autocomplete/SkAutocomplete.vue.d.ts +87 -0
- package/dist/components/Autocomplete/SkAutocompleteEmpty.vue.d.ts +17 -0
- package/dist/components/Autocomplete/SkAutocompleteGroup.vue.d.ts +17 -0
- package/dist/components/Autocomplete/SkAutocompleteGroupLabel.vue.d.ts +17 -0
- package/dist/components/Autocomplete/SkAutocompleteItem.vue.d.ts +39 -0
- package/dist/components/Autocomplete/SkAutocompleteSeparator.vue.d.ts +2 -0
- package/dist/components/Autocomplete/index.d.ts +7 -0
- package/dist/components/Autocomplete/types.d.ts +3 -0
- package/dist/components/Breadcrumbs/context.d.ts +4 -0
- package/dist/components/Button/SkButton.vue.d.ts +8 -1
- package/dist/components/Button/types.d.ts +2 -0
- package/dist/components/Card/SkCard.vue.d.ts +1 -1
- package/dist/components/ContextMenu/context.d.ts +3 -0
- package/dist/components/Dropdown/SkDropdown.vue.d.ts +1 -1
- package/dist/components/Dropdown/context.d.ts +3 -0
- package/dist/components/Field/SkField.vue.d.ts +7 -6
- package/dist/components/Input/SkInput.vue.d.ts +9 -2
- package/dist/components/Input/types.d.ts +2 -0
- package/dist/components/InputGroup/SkInputGroup.vue.d.ts +23 -0
- package/dist/components/InputGroup/SkInputGroupAddon.vue.d.ts +33 -0
- package/dist/components/InputGroup/types.d.ts +13 -0
- package/dist/components/NumberInput/SkNumberInput.vue.d.ts +15 -1
- package/dist/components/NumberInput/types.d.ts +2 -0
- package/dist/components/Pagination/context.d.ts +5 -0
- package/dist/components/Panel/SkPanel.vue.d.ts +1 -1
- package/dist/components/Panel/types.d.ts +2 -1
- package/dist/components/Radio/context.d.ts +4 -0
- package/dist/components/Select/SkSelect.vue.d.ts +7 -1
- package/dist/components/Select/types.d.ts +2 -0
- package/dist/components/Sidebar/SkSidebar.vue.d.ts +1 -1
- package/dist/components/Tabs/context.d.ts +6 -0
- package/dist/components/Textarea/SkTextarea.vue.d.ts +1 -1
- package/dist/components/Tooltip/SkTooltip.vue.d.ts +1 -1
- package/dist/composables/injectionKeys.d.ts +9 -0
- package/dist/global.d.ts +4 -0
- package/dist/index.d.ts +18 -0
- package/dist/sleekspace-ui.css +836 -280
- package/dist/sleekspace-ui.es.js +3759 -2545
- package/dist/sleekspace-ui.umd.js +3765 -2543
- package/dist/static/components/alert.d.ts +2 -1
- package/dist/static/components/avatar.d.ts +2 -1
- package/dist/static/components/breadcrumbs.d.ts +2 -1
- package/dist/static/components/button.d.ts +4 -2
- package/dist/static/components/card.d.ts +2 -1
- package/dist/static/components/checkbox.d.ts +2 -1
- package/dist/static/components/colorPicker.d.ts +2 -1
- package/dist/static/components/divider.d.ts +2 -1
- package/dist/static/components/dropdown.d.ts +2 -1
- package/dist/static/components/field.d.ts +2 -1
- package/dist/static/components/group.d.ts +2 -1
- package/dist/static/components/input.d.ts +4 -2
- package/dist/static/components/inputGroup.d.ts +8 -0
- package/dist/static/components/inputGroupAddon.d.ts +7 -0
- package/dist/static/components/navBar.d.ts +2 -1
- package/dist/static/components/numberInput.d.ts +4 -2
- package/dist/static/components/page.d.ts +2 -1
- package/dist/static/components/pagination.d.ts +2 -1
- package/dist/static/components/panel.d.ts +2 -1
- package/dist/static/components/progress.d.ts +2 -1
- package/dist/static/components/radio.d.ts +2 -1
- package/dist/static/components/select.d.ts +4 -2
- package/dist/static/components/sidebar.d.ts +2 -1
- package/dist/static/components/skeleton.d.ts +2 -1
- package/dist/static/components/slider.d.ts +2 -1
- package/dist/static/components/spinner.d.ts +2 -1
- package/dist/static/components/switchInput.d.ts +2 -1
- package/dist/static/components/table.d.ts +2 -1
- package/dist/static/components/tag.d.ts +2 -1
- package/dist/static/components/tagsInput.d.ts +2 -1
- package/dist/static/components/textarea.d.ts +2 -1
- package/dist/static/components/toolbar.d.ts +2 -1
- package/dist/static/components/tooltip.d.ts +2 -1
- package/dist/static/h.d.ts +2 -0
- package/dist/static/index.cjs.js +1 -1
- package/dist/static/index.d.ts +6 -0
- package/dist/static/index.es.js +366 -216
- package/dist/static/render.d.ts +2 -1
- package/dist/static/stringH.d.ts +2 -0
- package/dist/static/types.d.ts +5 -0
- package/dist/tailwind.css +222 -0
- package/dist/tokens.css +0 -223
- package/dist/types/corners.d.ts +1 -0
- package/dist/utils/slots.d.ts +6 -0
- package/llms-full.txt +17 -9
- package/package.json +9 -3
- package/src/components/Accordion/SkAccordion.vue +5 -2
- package/src/components/Accordion/SkAccordionItem.vue +7 -4
- package/src/components/Accordion/context.ts +23 -0
- package/src/components/Alert/SkAlert.vue +4 -2
- package/src/components/Autocomplete/SkAutocomplete.test.ts +83 -0
- package/src/components/Autocomplete/SkAutocomplete.vue +305 -0
- package/src/components/Autocomplete/SkAutocompleteEmpty.vue +39 -0
- package/src/components/Autocomplete/SkAutocompleteGroup.vue +46 -0
- package/src/components/Autocomplete/SkAutocompleteGroupLabel.vue +39 -0
- package/src/components/Autocomplete/SkAutocompleteItem.vue +85 -0
- package/src/components/Autocomplete/SkAutocompleteSeparator.vue +39 -0
- package/src/components/Autocomplete/index.ts +13 -0
- package/src/components/Autocomplete/types.ts +10 -0
- package/src/components/Breadcrumbs/SkBreadcrumbItem.vue +8 -3
- package/src/components/Breadcrumbs/SkBreadcrumbSeparator.vue +8 -2
- package/src/components/Breadcrumbs/SkBreadcrumbs.vue +11 -14
- package/src/components/Breadcrumbs/context.ts +20 -0
- package/src/components/Button/SkButton.vue +54 -11
- package/src/components/Button/types.ts +6 -0
- package/src/components/Card/SkCard.vue +12 -5
- package/src/components/Checkbox/SkCheckbox.vue +9 -2
- package/src/components/ColorPicker/SkColorPicker.vue +27 -5
- package/src/components/ContextMenu/SkContextMenu.vue +4 -1
- package/src/components/ContextMenu/SkContextMenuSubmenu.vue +5 -2
- package/src/components/ContextMenu/context.ts +17 -0
- package/src/components/Dropdown/SkDropdown.vue +2 -1
- package/src/components/Dropdown/SkDropdownSubmenu.vue +4 -3
- package/src/components/Dropdown/context.ts +16 -0
- package/src/components/Field/SkField.test.ts +88 -0
- package/src/components/Field/SkField.vue +15 -7
- package/src/components/Input/SkInput.test.ts +61 -0
- package/src/components/Input/SkInput.vue +42 -7
- package/src/components/Input/types.ts +2 -0
- package/src/components/InputGroup/SkInputGroup.test.ts +171 -0
- package/src/components/InputGroup/SkInputGroup.vue +131 -0
- package/src/components/InputGroup/SkInputGroupAddon.test.ts +104 -0
- package/src/components/InputGroup/SkInputGroupAddon.vue +107 -0
- package/src/components/InputGroup/types.ts +27 -0
- package/src/components/Listbox/SkListbox.vue +27 -6
- package/src/components/Modal/SkModal.vue +11 -4
- package/src/components/NavBar/SkNavBar.vue +5 -4
- package/src/components/NumberInput/SkNumberInput.vue +49 -8
- package/src/components/NumberInput/types.ts +2 -0
- package/src/components/Page/SkPage.vue +18 -15
- package/src/components/Pagination/SkPagination.vue +6 -3
- package/src/components/Pagination/SkPaginationItem.vue +8 -5
- package/src/components/Pagination/context.ts +19 -0
- package/src/components/Panel/types.ts +3 -2
- package/src/components/Popover/SkPopover.vue +11 -4
- package/src/components/Radio/SkRadio.vue +14 -4
- package/src/components/Radio/SkRadioGroup.vue +4 -2
- package/src/components/Radio/context.ts +17 -0
- package/src/components/Select/SkSelect.vue +39 -7
- package/src/components/Select/types.ts +2 -0
- package/src/components/Switch/SkSwitch.vue +14 -13
- package/src/components/Tabs/SkTab.vue +10 -3
- package/src/components/Tabs/SkTabList.vue +4 -2
- package/src/components/Tabs/SkTabs.vue +5 -3
- package/src/components/Tabs/context.ts +19 -0
- package/src/components/TagsInput/SkTagsInput.vue +28 -7
- package/src/components/Textarea/SkTextarea.vue +27 -6
- package/src/components/TreeView/SkTreeItem.vue +10 -2
- package/src/composables/injectionKeys.ts +52 -0
- package/src/index.ts +28 -0
- package/src/static/__tests__/parity.test.ts +2 -1
- package/src/static/__tests__/parityHarness.ts +5 -2
- package/src/static/components/__tests__/helpers.test.ts +191 -99
- package/src/static/components/alert.ts +12 -11
- package/src/static/components/avatar.ts +15 -16
- package/src/static/components/breadcrumbs.ts +3 -2
- package/src/static/components/button.ts +23 -27
- package/src/static/components/card.ts +3 -2
- package/src/static/components/checkbox.ts +11 -14
- package/src/static/components/colorPicker.ts +7 -9
- package/src/static/components/divider.ts +4 -3
- package/src/static/components/dropdown.ts +15 -6
- package/src/static/components/field.ts +32 -15
- package/src/static/components/group.ts +3 -2
- package/src/static/components/input.ts +20 -15
- package/src/static/components/inputGroup.ts +30 -0
- package/src/static/components/inputGroupAddon.ts +29 -0
- package/src/static/components/navBar.ts +30 -17
- package/src/static/components/numberInput.ts +17 -17
- package/src/static/components/page.ts +3 -2
- package/src/static/components/pagination.ts +3 -2
- package/src/static/components/panel.ts +3 -2
- package/src/static/components/progress.ts +3 -2
- package/src/static/components/radio.ts +14 -20
- package/src/static/components/select.ts +18 -15
- package/src/static/components/sidebar.ts +9 -13
- package/src/static/components/skeleton.ts +7 -10
- package/src/static/components/slider.ts +7 -9
- package/src/static/components/spinner.ts +22 -22
- package/src/static/components/switchInput.ts +12 -14
- package/src/static/components/table.ts +8 -10
- package/src/static/components/tag.ts +17 -11
- package/src/static/components/tagsInput.ts +3 -3
- package/src/static/components/textarea.ts +8 -13
- package/src/static/components/toolbar.ts +7 -10
- package/src/static/components/tooltip.ts +3 -2
- package/src/static/generated/defaults.ts +25 -9
- package/src/static/generated/propTypes.ts +19 -2
- package/src/static/h.ts +16 -0
- package/src/static/index.ts +8 -0
- package/src/static/render.test.ts +14 -10
- package/src/static/render.ts +33 -18
- package/src/static/specs.test.ts +1 -0
- package/src/static/specs.ts +22 -2
- package/src/static/stringH.ts +104 -0
- package/src/static/types.ts +25 -0
- package/src/styles/components/_autocomplete.scss +498 -0
- package/src/styles/components/_button.scss +55 -6
- package/src/styles/components/_index.scss +2 -0
- package/src/styles/components/_input-group.scss +292 -0
- package/src/styles/components/_input.scss +57 -9
- package/src/styles/components/_number-input.scss +88 -14
- package/src/styles/components/_select.scss +56 -9
- package/src/styles/mixins/_cut-border.scss +83 -0
- package/src/styles/tailwind.scss +262 -0
- package/src/styles/tokens.scss +8 -255
- package/src/types/corners.ts +10 -0
- package/src/utils/slots.test.ts +89 -0
- package/src/utils/slots.ts +80 -0
- package/web-types.json +392 -12
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
<!----------------------------------------------------------------------------------------------------------------------
|
|
2
|
+
- InputGroup Component
|
|
3
|
+
--------------------------------------------------------------------------------------------------------------------->
|
|
4
|
+
|
|
5
|
+
<template>
|
|
6
|
+
<div :class="classes" :style="customColorStyles">
|
|
7
|
+
<slot />
|
|
8
|
+
</div>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<!--------------------------------------------------------------------------------------------------------------------->
|
|
12
|
+
|
|
13
|
+
<style lang="scss" scoped>
|
|
14
|
+
// Component styles are implemented in /src/styles/components/_input-group.scss
|
|
15
|
+
</style>
|
|
16
|
+
|
|
17
|
+
<!--------------------------------------------------------------------------------------------------------------------->
|
|
18
|
+
|
|
19
|
+
<script setup lang="ts">
|
|
20
|
+
/**
|
|
21
|
+
* @component SkInputGroup
|
|
22
|
+
* @description Wraps form inputs, buttons, selects, and SkInputGroupAddon children into a
|
|
23
|
+
* single visually unified control. Bevels appear only on the group's outer corners; interior
|
|
24
|
+
* joins are clean. Cascades kind and size to Vue children via provide/inject — size on the
|
|
25
|
+
* `input-group-size` channel, kind on the `inherited-kind` ambient-default channel. A
|
|
26
|
+
* parent SkField's validation override (the `validation-kind` channel) takes precedence over
|
|
27
|
+
* both the group's own kind and the descendants' kinds.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```vue
|
|
31
|
+
* <SkInputGroup :corners="['top-left', 'bottom-right']" size="md" kind="primary">
|
|
32
|
+
* <SkInputGroupAddon>$</SkInputGroupAddon>
|
|
33
|
+
* <SkInput v-model="amount" />
|
|
34
|
+
* <SkButton>Submit</SkButton>
|
|
35
|
+
* </SkInputGroup>
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @slot default - Form components, SkButton, and SkInputGroupAddon children in left-to-right order.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
import { computed, inject, provide, toRef } from 'vue';
|
|
42
|
+
|
|
43
|
+
// Types
|
|
44
|
+
import type {
|
|
45
|
+
SkInputGroupBaseProps,
|
|
46
|
+
SkInputGroupCorner,
|
|
47
|
+
SkInputGroupKind,
|
|
48
|
+
SkInputGroupSize,
|
|
49
|
+
} from './types';
|
|
50
|
+
|
|
51
|
+
export type { SkInputGroupComponentProps } from './types';
|
|
52
|
+
|
|
53
|
+
// Composables
|
|
54
|
+
import {
|
|
55
|
+
NO_KIND,
|
|
56
|
+
NO_SIZE,
|
|
57
|
+
inheritedKindKey,
|
|
58
|
+
inputGroupSizeKey,
|
|
59
|
+
validationKindKey,
|
|
60
|
+
} from '@/composables/injectionKeys';
|
|
61
|
+
import { useCustomColors } from '@/composables/useCustomColors';
|
|
62
|
+
|
|
63
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
const props = withDefaults(defineProps<SkInputGroupBaseProps>(), {
|
|
66
|
+
kind: undefined,
|
|
67
|
+
size: 'md',
|
|
68
|
+
corners: () => [ 'top-left', 'bottom-right' ],
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
// Same resolution rule as the leaf form components: validation override (from a parent
|
|
74
|
+
// SkField with `state` set) wins, then explicit prop, then ambient inherited-kind, then
|
|
75
|
+
// 'neutral'. Validation-kind passes through to descendants on its own channel — we never
|
|
76
|
+
// re-provide it.
|
|
77
|
+
const validationKind = inject(validationKindKey, NO_KIND);
|
|
78
|
+
const inheritedKind = inject(inheritedKindKey, NO_KIND);
|
|
79
|
+
|
|
80
|
+
const effectiveKind = computed<SkInputGroupKind>(() =>
|
|
81
|
+
{
|
|
82
|
+
if(validationKind.value !== undefined) { return validationKind.value; }
|
|
83
|
+
if(props.kind !== undefined) { return props.kind; }
|
|
84
|
+
if(inheritedKind.value !== undefined) { return inheritedKind.value; }
|
|
85
|
+
return 'neutral';
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
// Re-provide on the inherited-kind channel so descendants pick up the group's prop-driven
|
|
91
|
+
// kind as their default. Use the prop directly (not the resolved value) so a parent SkField's
|
|
92
|
+
// validation override doesn't propagate as the group's "ambient default" and clobber children
|
|
93
|
+
// that have their own explicit kind.
|
|
94
|
+
const ambientKind = computed<SkInputGroupKind | undefined>(() =>
|
|
95
|
+
{
|
|
96
|
+
if(props.kind !== undefined) { return props.kind; }
|
|
97
|
+
return inheritedKind.value;
|
|
98
|
+
});
|
|
99
|
+
provide(inheritedKindKey, ambientKind);
|
|
100
|
+
|
|
101
|
+
// Size passes straight through to the input-group-size channel — no resolution to do.
|
|
102
|
+
const sizeRef = toRef(props, 'size');
|
|
103
|
+
provide(inputGroupSizeKey, sizeRef);
|
|
104
|
+
|
|
105
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
function hasCorner(corner : SkInputGroupCorner) : boolean
|
|
108
|
+
{
|
|
109
|
+
return props.corners.includes(corner);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const classes = computed<Record<string, boolean>>(() => ({
|
|
113
|
+
'sk-input-group': true,
|
|
114
|
+
[`sk-${ effectiveKind.value }`]: true,
|
|
115
|
+
[`sk-${ sizeRef.value }`]: true,
|
|
116
|
+
'sk-cut-top-left': hasCorner('top-left'),
|
|
117
|
+
'sk-cut-top-right': hasCorner('top-right'),
|
|
118
|
+
'sk-cut-bottom-right': hasCorner('bottom-right'),
|
|
119
|
+
'sk-cut-bottom-left': hasCorner('bottom-left'),
|
|
120
|
+
}));
|
|
121
|
+
|
|
122
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
123
|
+
|
|
124
|
+
const customColorStyles = useCustomColors(
|
|
125
|
+
'input-group',
|
|
126
|
+
toRef(() => props.baseColor),
|
|
127
|
+
toRef(() => props.textColor)
|
|
128
|
+
);
|
|
129
|
+
</script>
|
|
130
|
+
|
|
131
|
+
<!--------------------------------------------------------------------------------------------------------------------->
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { type InjectionKey, computed, defineComponent, h, provide } from 'vue';
|
|
2
|
+
import { mount } from '@vue/test-utils';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
|
|
5
|
+
import { inheritedKindKey, inputGroupSizeKey, validationKindKey } from '@/composables/injectionKeys';
|
|
6
|
+
|
|
7
|
+
import SkInputGroupAddon from './SkInputGroupAddon.vue';
|
|
8
|
+
|
|
9
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
function withProvides(
|
|
12
|
+
children : () => ReturnType<typeof h>,
|
|
13
|
+
provides : Map<InjectionKey<unknown>, unknown> = new Map<InjectionKey<unknown>, unknown>()
|
|
14
|
+
) : ReturnType<typeof defineComponent>
|
|
15
|
+
{
|
|
16
|
+
return defineComponent({
|
|
17
|
+
setup()
|
|
18
|
+
{
|
|
19
|
+
for(const [ key, value ] of provides)
|
|
20
|
+
{
|
|
21
|
+
provide(key, value);
|
|
22
|
+
}
|
|
23
|
+
return () => h('div', children());
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
describe('SkInputGroupAddon', () =>
|
|
31
|
+
{
|
|
32
|
+
it('renders a span with the addon class', () =>
|
|
33
|
+
{
|
|
34
|
+
const wrapper = mount(SkInputGroupAddon, { slots: { default: '$' } });
|
|
35
|
+
const span = wrapper.find('span.sk-input-group-addon');
|
|
36
|
+
expect(span.exists()).toBe(true);
|
|
37
|
+
expect(span.text()).toBe('$');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('uses injected input-group-size when no explicit size is set', () =>
|
|
41
|
+
{
|
|
42
|
+
const Provider = withProvides(
|
|
43
|
+
() => h(SkInputGroupAddon, null, { default: () => '$' }),
|
|
44
|
+
new Map<InjectionKey<unknown>, unknown>([ [ inputGroupSizeKey, computed(() => 'lg') ] ])
|
|
45
|
+
);
|
|
46
|
+
const wrapper = mount(Provider);
|
|
47
|
+
const span = wrapper.find('span.sk-input-group-addon');
|
|
48
|
+
expect(span.classes()).toContain('sk-lg');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('uses injected inherited-kind when no explicit kind is set', () =>
|
|
52
|
+
{
|
|
53
|
+
const Provider = withProvides(
|
|
54
|
+
() => h(SkInputGroupAddon, null, { default: () => '$' }),
|
|
55
|
+
new Map<InjectionKey<unknown>, unknown>([ [ inheritedKindKey, computed(() => 'primary') ] ])
|
|
56
|
+
);
|
|
57
|
+
const wrapper = mount(Provider);
|
|
58
|
+
const span = wrapper.find('span.sk-input-group-addon');
|
|
59
|
+
expect(span.classes()).toContain('sk-primary');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('validation-kind always wins over explicit kind and inherited-kind', () =>
|
|
63
|
+
{
|
|
64
|
+
const Provider = withProvides(
|
|
65
|
+
() => h(SkInputGroupAddon, { kind: 'success' }, { default: () => '$' }),
|
|
66
|
+
new Map<InjectionKey<unknown>, unknown>([
|
|
67
|
+
[ validationKindKey, computed(() => 'danger') ],
|
|
68
|
+
[ inheritedKindKey, computed(() => 'primary') ],
|
|
69
|
+
])
|
|
70
|
+
);
|
|
71
|
+
const wrapper = mount(Provider);
|
|
72
|
+
const span = wrapper.find('span.sk-input-group-addon');
|
|
73
|
+
expect(span.classes()).toContain('sk-danger');
|
|
74
|
+
expect(span.classes()).not.toContain('sk-success');
|
|
75
|
+
expect(span.classes()).not.toContain('sk-primary');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('explicit props override inherited-kind and input-group-size', () =>
|
|
79
|
+
{
|
|
80
|
+
const Provider = withProvides(
|
|
81
|
+
() => h(SkInputGroupAddon, { size: 'sm', kind: 'danger' }, { default: () => '$' }),
|
|
82
|
+
new Map<InjectionKey<unknown>, unknown>([
|
|
83
|
+
[ inputGroupSizeKey, computed(() => 'lg') ],
|
|
84
|
+
[ inheritedKindKey, computed(() => 'primary') ],
|
|
85
|
+
])
|
|
86
|
+
);
|
|
87
|
+
const wrapper = mount(Provider);
|
|
88
|
+
const span = wrapper.find('span.sk-input-group-addon');
|
|
89
|
+
expect(span.classes()).toContain('sk-sm');
|
|
90
|
+
expect(span.classes()).toContain('sk-danger');
|
|
91
|
+
expect(span.classes()).not.toContain('sk-lg');
|
|
92
|
+
expect(span.classes()).not.toContain('sk-primary');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('defaults to md/neutral when nothing is provided', () =>
|
|
96
|
+
{
|
|
97
|
+
const wrapper = mount(SkInputGroupAddon);
|
|
98
|
+
const span = wrapper.find('span.sk-input-group-addon');
|
|
99
|
+
expect(span.classes()).toContain('sk-md');
|
|
100
|
+
expect(span.classes()).toContain('sk-neutral');
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<!----------------------------------------------------------------------------------------------------------------------
|
|
2
|
+
- InputGroup Addon Component
|
|
3
|
+
--------------------------------------------------------------------------------------------------------------------->
|
|
4
|
+
|
|
5
|
+
<template>
|
|
6
|
+
<span :class="classes">
|
|
7
|
+
<slot />
|
|
8
|
+
</span>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<!--------------------------------------------------------------------------------------------------------------------->
|
|
12
|
+
|
|
13
|
+
<style lang="scss" scoped>
|
|
14
|
+
// Component styles are implemented in /src/styles/components/_input-group.scss
|
|
15
|
+
</style>
|
|
16
|
+
|
|
17
|
+
<!--------------------------------------------------------------------------------------------------------------------->
|
|
18
|
+
|
|
19
|
+
<script setup lang="ts">
|
|
20
|
+
/**
|
|
21
|
+
* @component SkInputGroupAddon
|
|
22
|
+
* @description Styled wrapper for non-component content (text, icons, plain HTML) inside an
|
|
23
|
+
* SkInputGroup. Renders as a span with the addon background, border, and font sizing matching
|
|
24
|
+
* the surrounding group. Inherits size and kind from the parent SkInputGroup via inject.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```vue
|
|
28
|
+
* <SkInputGroup>
|
|
29
|
+
* <SkInputGroupAddon>$</SkInputGroupAddon>
|
|
30
|
+
* <SkInput v-model="amount" />
|
|
31
|
+
* </SkInputGroup>
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @slot default - Addon content. Text, icons, or any inline element.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
import { computed, inject } from 'vue';
|
|
38
|
+
|
|
39
|
+
// Types
|
|
40
|
+
import type { SkInputGroupAddonKind, SkInputGroupAddonSize } from './types';
|
|
41
|
+
|
|
42
|
+
// Composables
|
|
43
|
+
import {
|
|
44
|
+
NO_KIND,
|
|
45
|
+
NO_SIZE,
|
|
46
|
+
inheritedKindKey,
|
|
47
|
+
inputGroupSizeKey,
|
|
48
|
+
validationKindKey,
|
|
49
|
+
} from '@/composables/injectionKeys';
|
|
50
|
+
|
|
51
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
export interface SkInputGroupAddonComponentProps
|
|
54
|
+
{
|
|
55
|
+
/**
|
|
56
|
+
* Semantic color kind. Inherits from a parent SkInputGroup or SkField when not set.
|
|
57
|
+
* @default inherited or 'neutral'
|
|
58
|
+
*/
|
|
59
|
+
kind ?: SkInputGroupAddonKind;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Size of the addon. Inherits from a parent SkInputGroup when not set.
|
|
63
|
+
* @default inherited or 'md'
|
|
64
|
+
*/
|
|
65
|
+
size ?: SkInputGroupAddonSize;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
const props = withDefaults(defineProps<SkInputGroupAddonComponentProps>(), {
|
|
71
|
+
kind: undefined,
|
|
72
|
+
size: undefined,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
76
|
+
|
|
77
|
+
const validationKind = inject(validationKindKey, NO_KIND);
|
|
78
|
+
const inheritedKind = inject(inheritedKindKey, NO_KIND);
|
|
79
|
+
const inputGroupSize = inject(inputGroupSizeKey, NO_SIZE);
|
|
80
|
+
|
|
81
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
const effectiveKind = computed<SkInputGroupAddonKind>(() =>
|
|
84
|
+
{
|
|
85
|
+
if(validationKind.value !== undefined) { return validationKind.value; }
|
|
86
|
+
if(props.kind !== undefined) { return props.kind; }
|
|
87
|
+
if(inheritedKind.value !== undefined) { return inheritedKind.value; }
|
|
88
|
+
return 'neutral';
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const effectiveSize = computed<SkInputGroupAddonSize>(() =>
|
|
92
|
+
{
|
|
93
|
+
if(props.size !== undefined) { return props.size; }
|
|
94
|
+
if(inputGroupSize.value !== undefined) { return inputGroupSize.value; }
|
|
95
|
+
return 'md';
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
const classes = computed<Record<string, boolean>>(() => ({
|
|
101
|
+
'sk-input-group-addon': true,
|
|
102
|
+
[`sk-${ effectiveKind.value }`]: true,
|
|
103
|
+
[`sk-${ effectiveSize.value }`]: true,
|
|
104
|
+
}));
|
|
105
|
+
</script>
|
|
106
|
+
|
|
107
|
+
<!--------------------------------------------------------------------------------------------------------------------->
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
2
|
+
// InputGroup Component Types
|
|
3
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
import type { ComponentCustomColors, ComponentKind, ComponentSize } from '@/types';
|
|
6
|
+
import type { SkCorner } from '@/types/corners';
|
|
7
|
+
|
|
8
|
+
export type SkInputGroupKind = ComponentKind;
|
|
9
|
+
export type SkInputGroupSize = ComponentSize;
|
|
10
|
+
|
|
11
|
+
export type SkInputGroupCorner = SkCorner;
|
|
12
|
+
|
|
13
|
+
export type SkInputGroupAddonKind = SkInputGroupKind;
|
|
14
|
+
export type SkInputGroupAddonSize = SkInputGroupSize;
|
|
15
|
+
|
|
16
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
export interface SkInputGroupBaseProps extends ComponentCustomColors
|
|
19
|
+
{
|
|
20
|
+
kind ?: SkInputGroupKind;
|
|
21
|
+
size ?: SkInputGroupSize;
|
|
22
|
+
corners ?: SkInputGroupCorner[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type SkInputGroupComponentProps = SkInputGroupBaseProps;
|
|
26
|
+
|
|
27
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
* to create visual dividers between groups of options.
|
|
72
72
|
*/
|
|
73
73
|
|
|
74
|
-
import {
|
|
74
|
+
import { computed, inject, toRef } from 'vue';
|
|
75
75
|
import {
|
|
76
76
|
ComboboxAnchor,
|
|
77
77
|
ComboboxContent,
|
|
@@ -87,6 +87,13 @@
|
|
|
87
87
|
import type { SkListboxKind, SkListboxSize } from './types';
|
|
88
88
|
|
|
89
89
|
// Composables
|
|
90
|
+
import {
|
|
91
|
+
NO_KIND,
|
|
92
|
+
NO_SIZE,
|
|
93
|
+
inheritedKindKey,
|
|
94
|
+
inputGroupSizeKey,
|
|
95
|
+
validationKindKey,
|
|
96
|
+
} from '@/composables/injectionKeys';
|
|
90
97
|
import { useCustomColors } from '@/composables/useCustomColors';
|
|
91
98
|
import { usePortalContext } from '@/composables/usePortalContext';
|
|
92
99
|
|
|
@@ -131,7 +138,7 @@
|
|
|
131
138
|
|
|
132
139
|
const props = withDefaults(defineProps<SkListboxComponentProps>(), {
|
|
133
140
|
kind: undefined,
|
|
134
|
-
size:
|
|
141
|
+
size: undefined,
|
|
135
142
|
placeholder: 'Search...',
|
|
136
143
|
disabled: false,
|
|
137
144
|
});
|
|
@@ -150,19 +157,33 @@
|
|
|
150
157
|
// Handle portal context (theme injection/re-provision for nested portal components)
|
|
151
158
|
const { theme } = usePortalContext();
|
|
152
159
|
|
|
153
|
-
|
|
154
|
-
const
|
|
160
|
+
const validationKind = inject(validationKindKey, NO_KIND);
|
|
161
|
+
const inheritedKind = inject(inheritedKindKey, NO_KIND);
|
|
162
|
+
const inputGroupSize = inject(inputGroupSizeKey, NO_SIZE);
|
|
155
163
|
|
|
156
164
|
//------------------------------------------------------------------------------------------------------------------
|
|
157
165
|
|
|
158
|
-
const effectiveKind = computed(() =>
|
|
166
|
+
const effectiveKind = computed<SkListboxKind>(() =>
|
|
167
|
+
{
|
|
168
|
+
if(validationKind.value !== undefined) { return validationKind.value; }
|
|
169
|
+
if(props.kind !== undefined) { return props.kind; }
|
|
170
|
+
if(inheritedKind.value !== undefined) { return inheritedKind.value; }
|
|
171
|
+
return 'neutral';
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const effectiveSize = computed<SkListboxSize>(() =>
|
|
175
|
+
{
|
|
176
|
+
if(props.size !== undefined) { return props.size; }
|
|
177
|
+
if(inputGroupSize.value !== undefined) { return inputGroupSize.value; }
|
|
178
|
+
return 'md';
|
|
179
|
+
});
|
|
159
180
|
|
|
160
181
|
//------------------------------------------------------------------------------------------------------------------
|
|
161
182
|
|
|
162
183
|
const wrapperClasses = computed(() => ({
|
|
163
184
|
'sk-listbox': true,
|
|
164
185
|
[`sk-${ effectiveKind.value }`]: true,
|
|
165
|
-
[`sk-${
|
|
186
|
+
[`sk-${ effectiveSize.value }`]: true,
|
|
166
187
|
}));
|
|
167
188
|
|
|
168
189
|
//------------------------------------------------------------------------------------------------------------------
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<DialogRoot v-model:open="isOpen">
|
|
3
|
-
<DialogTrigger v-if="
|
|
3
|
+
<DialogTrigger v-if="hasSlotContent(slots.trigger) || triggerText" as-child>
|
|
4
4
|
<slot name="trigger">
|
|
5
5
|
<SkButton :kind="kind">
|
|
6
6
|
{{ triggerText }}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
@pointer-down-outside="handleOverlayClick"
|
|
18
18
|
@interact-outside="handleOverlayClick"
|
|
19
19
|
>
|
|
20
|
-
<div v-if="
|
|
20
|
+
<div v-if="hasSlotContent(slots.title) || title" class="sk-modal-header">
|
|
21
21
|
<DialogTitle class="sk-modal-title">
|
|
22
22
|
<slot name="title" :close="close">
|
|
23
23
|
{{ title }}
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
<div class="sk-modal-body">
|
|
41
41
|
<slot :close="close" />
|
|
42
42
|
</div>
|
|
43
|
-
<div v-if="
|
|
43
|
+
<div v-if="hasSlotContent(slots.footer)" class="sk-modal-footer">
|
|
44
44
|
<slot name="footer" :close="close" />
|
|
45
45
|
</div>
|
|
46
46
|
</DialogContent>
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
* @slot footer - Footer content, typically containing action buttons. Receives `{ close }` slot prop.
|
|
89
89
|
*/
|
|
90
90
|
|
|
91
|
-
import { computed, ref, toRef, watch } from 'vue';
|
|
91
|
+
import { type Slots, computed, ref, toRef, useSlots, watch } from 'vue';
|
|
92
92
|
import {
|
|
93
93
|
DialogClose,
|
|
94
94
|
DialogContent,
|
|
@@ -106,6 +106,9 @@
|
|
|
106
106
|
import { useCustomColors } from '@/composables/useCustomColors';
|
|
107
107
|
import { usePortalContext } from '@/composables/usePortalContext';
|
|
108
108
|
|
|
109
|
+
// Utils
|
|
110
|
+
import { hasSlotContent } from '@/utils/slots';
|
|
111
|
+
|
|
109
112
|
// Components
|
|
110
113
|
import SkButton from '../Button/SkButton.vue';
|
|
111
114
|
|
|
@@ -206,6 +209,10 @@
|
|
|
206
209
|
|
|
207
210
|
//------------------------------------------------------------------------------------------------------------------
|
|
208
211
|
|
|
212
|
+
const slots : Slots = useSlots();
|
|
213
|
+
|
|
214
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
215
|
+
|
|
209
216
|
const isOpen = ref(props.open);
|
|
210
217
|
|
|
211
218
|
watch(() => props.open, (newValue) =>
|
|
@@ -5,19 +5,19 @@
|
|
|
5
5
|
<template>
|
|
6
6
|
<nav :class="classes" :style="customColorStyles">
|
|
7
7
|
<div class="sk-navbar-content">
|
|
8
|
-
<div v-if="slots.leading" class="sk-navbar-leading">
|
|
8
|
+
<div v-if="hasSlotContent(slots.leading)" class="sk-navbar-leading">
|
|
9
9
|
<slot name="leading" />
|
|
10
10
|
</div>
|
|
11
11
|
|
|
12
|
-
<div v-if="slots.brand" class="sk-navbar-brand">
|
|
12
|
+
<div v-if="hasSlotContent(slots.brand)" class="sk-navbar-brand">
|
|
13
13
|
<slot name="brand" />
|
|
14
14
|
</div>
|
|
15
15
|
|
|
16
|
-
<div v-if="slots.default" class="sk-navbar-nav">
|
|
16
|
+
<div v-if="hasSlotContent(slots.default)" class="sk-navbar-nav">
|
|
17
17
|
<slot />
|
|
18
18
|
</div>
|
|
19
19
|
|
|
20
|
-
<div v-if="slots.actions" class="sk-navbar-actions">
|
|
20
|
+
<div v-if="hasSlotContent(slots.actions)" class="sk-navbar-actions">
|
|
21
21
|
<slot name="actions" />
|
|
22
22
|
</div>
|
|
23
23
|
</div>
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
import type { SkNavBarKind, SkNavBarSize } from './types';
|
|
63
63
|
import type { ComponentCustomColors } from '@/types';
|
|
64
64
|
import { useCustomColors } from '@/composables/useCustomColors';
|
|
65
|
+
import { hasSlotContent } from '@/utils/slots';
|
|
65
66
|
import { NAVBAR_KIND_KEY, NAVBAR_SIZE_KEY } from './context';
|
|
66
67
|
|
|
67
68
|
//------------------------------------------------------------------------------------------------------------------
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
:step="step"
|
|
18
18
|
>
|
|
19
19
|
<NumberFieldInput :class="inputClasses" :placeholder="placeholder" />
|
|
20
|
-
<div class="sk-number-input-steppers">
|
|
20
|
+
<div v-if="showSteppers" class="sk-number-input-steppers">
|
|
21
21
|
<NumberFieldIncrement class="sk-number-input-increment">
|
|
22
22
|
<svg
|
|
23
23
|
xmlns="http://www.w3.org/2000/svg"
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
* ```
|
|
75
75
|
*/
|
|
76
76
|
|
|
77
|
-
import {
|
|
77
|
+
import { computed, inject, toRef } from 'vue';
|
|
78
78
|
import {
|
|
79
79
|
NumberFieldDecrement,
|
|
80
80
|
NumberFieldIncrement,
|
|
@@ -84,9 +84,16 @@
|
|
|
84
84
|
|
|
85
85
|
// Types
|
|
86
86
|
import type { ComponentCustomColors } from '@/types';
|
|
87
|
-
import type { SkNumberInputKind, SkNumberInputSize } from './types';
|
|
87
|
+
import type { SkNumberInputCorner, SkNumberInputKind, SkNumberInputSize } from './types';
|
|
88
88
|
|
|
89
89
|
// Composables
|
|
90
|
+
import {
|
|
91
|
+
NO_KIND,
|
|
92
|
+
NO_SIZE,
|
|
93
|
+
inheritedKindKey,
|
|
94
|
+
inputGroupSizeKey,
|
|
95
|
+
validationKindKey,
|
|
96
|
+
} from '@/composables/injectionKeys';
|
|
90
97
|
import { useCustomColors } from '@/composables/useCustomColors';
|
|
91
98
|
|
|
92
99
|
//------------------------------------------------------------------------------------------------------------------
|
|
@@ -170,13 +177,27 @@
|
|
|
170
177
|
* @default 1
|
|
171
178
|
*/
|
|
172
179
|
step ?: number;
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* When true, renders the increment/decrement stepper buttons on the right side of the
|
|
183
|
+
* input. Set to false for a plain numeric text field (users can still use arrow keys
|
|
184
|
+
* and type values directly).
|
|
185
|
+
* @default true
|
|
186
|
+
*/
|
|
187
|
+
showSteppers ?: boolean;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Which corners receive the beveled cut. Pass an empty array for square corners.
|
|
191
|
+
* @default undefined (renders as ['top-right'])
|
|
192
|
+
*/
|
|
193
|
+
corners ?: SkNumberInputCorner[];
|
|
173
194
|
}
|
|
174
195
|
|
|
175
196
|
//------------------------------------------------------------------------------------------------------------------
|
|
176
197
|
|
|
177
198
|
const props = withDefaults(defineProps<SkNumberInputComponentProps>(), {
|
|
178
199
|
kind: undefined,
|
|
179
|
-
size:
|
|
200
|
+
size: undefined,
|
|
180
201
|
placeholder: undefined,
|
|
181
202
|
disabled: false,
|
|
182
203
|
readonly: false,
|
|
@@ -185,6 +206,8 @@
|
|
|
185
206
|
min: undefined,
|
|
186
207
|
max: undefined,
|
|
187
208
|
step: 1,
|
|
209
|
+
showSteppers: true,
|
|
210
|
+
corners: undefined,
|
|
188
211
|
});
|
|
189
212
|
|
|
190
213
|
//------------------------------------------------------------------------------------------------------------------
|
|
@@ -198,19 +221,37 @@
|
|
|
198
221
|
|
|
199
222
|
//------------------------------------------------------------------------------------------------------------------
|
|
200
223
|
|
|
201
|
-
|
|
202
|
-
const
|
|
224
|
+
const validationKind = inject(validationKindKey, NO_KIND);
|
|
225
|
+
const inheritedKind = inject(inheritedKindKey, NO_KIND);
|
|
226
|
+
const inputGroupSize = inject(inputGroupSizeKey, NO_SIZE);
|
|
203
227
|
|
|
204
228
|
//------------------------------------------------------------------------------------------------------------------
|
|
205
229
|
|
|
206
|
-
const effectiveKind = computed(() =>
|
|
230
|
+
const effectiveKind = computed<SkNumberInputKind>(() =>
|
|
231
|
+
{
|
|
232
|
+
if(validationKind.value !== undefined) { return validationKind.value; }
|
|
233
|
+
if(props.kind !== undefined) { return props.kind; }
|
|
234
|
+
if(inheritedKind.value !== undefined) { return inheritedKind.value; }
|
|
235
|
+
return 'neutral';
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const effectiveSize = computed<SkNumberInputSize>(() =>
|
|
239
|
+
{
|
|
240
|
+
if(props.size !== undefined) { return props.size; }
|
|
241
|
+
if(inputGroupSize.value !== undefined) { return inputGroupSize.value; }
|
|
242
|
+
return 'md';
|
|
243
|
+
});
|
|
207
244
|
|
|
208
245
|
//------------------------------------------------------------------------------------------------------------------
|
|
209
246
|
|
|
210
247
|
const wrapperClasses = computed(() => ({
|
|
211
248
|
'sk-number-input-wrapper': true,
|
|
212
249
|
[`sk-${ effectiveKind.value }`]: true,
|
|
213
|
-
[`sk-${
|
|
250
|
+
[`sk-${ effectiveSize.value }`]: true,
|
|
251
|
+
'sk-cut-top-left': props.corners?.includes('top-left') ?? false,
|
|
252
|
+
'sk-cut-top-right': props.corners?.includes('top-right') ?? false,
|
|
253
|
+
'sk-cut-bottom-right': props.corners?.includes('bottom-right') ?? false,
|
|
254
|
+
'sk-cut-bottom-left': props.corners?.includes('bottom-left') ?? false,
|
|
214
255
|
}));
|
|
215
256
|
|
|
216
257
|
//------------------------------------------------------------------------------------------------------------------
|
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
//----------------------------------------------------------------------------------------------------------------------
|
|
4
4
|
|
|
5
5
|
import type { ComponentKind, ComponentSize } from '@/types';
|
|
6
|
+
import type { SkCorner } from '@/types/corners';
|
|
6
7
|
|
|
7
8
|
export type SkNumberInputKind = ComponentKind;
|
|
8
9
|
export type SkNumberInputSize = ComponentSize;
|
|
10
|
+
export type SkNumberInputCorner = SkCorner;
|
|
9
11
|
|
|
10
12
|
//----------------------------------------------------------------------------------------------------------------------
|