milk-lib 0.0.14 → 0.0.16
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 +8 -0
- package/dist/components/ButtonMilk/ButtonMilk.scss +23 -21
- package/dist/components/Command/Command.types.d.ts +29 -0
- package/dist/components/Command/Command.types.js +1 -0
- package/dist/components/Command/Command.utils.d.ts +9 -0
- package/dist/components/Command/Command.utils.js +8 -0
- package/dist/components/Command/CommandGroup.svelte +43 -0
- package/dist/components/Command/CommandGroup.svelte.d.ts +4 -0
- package/dist/components/Command/CommandInput.svelte +60 -0
- package/dist/components/Command/CommandInput.svelte.d.ts +9 -0
- package/dist/components/Command/CommandItem.svelte +102 -0
- package/dist/components/Command/CommandItem.svelte.d.ts +4 -0
- package/dist/components/Command/CommandList.svelte +13 -0
- package/dist/components/Command/CommandList.svelte.d.ts +4 -0
- package/dist/components/Command/CommandRoot.svelte +107 -0
- package/dist/components/Command/CommandRoot.svelte.d.ts +4 -0
- package/dist/components/Command/index.d.ts +6 -0
- package/dist/components/Command/index.js +6 -0
- package/dist/components/TextInputBlock/TextInputBlock.svelte +109 -0
- package/dist/components/TextInputBlock/TextInputBlock.svelte.d.ts +6 -0
- package/dist/components/TextInputBlock/TextInputBlock.types.d.ts +8 -0
- package/dist/components/TextInputBlock/TextInputBlock.types.js +1 -0
- package/dist/components/TextInputBlock/index.d.ts +2 -0
- package/dist/components/TextInputBlock/index.js +2 -0
- package/dist/components/TextInputMilk/TextInputMilk.scss +81 -0
- package/dist/components/TextInputMilk/TextInputMilk.svelte +39 -0
- package/dist/components/TextInputMilk/TextInputMilk.svelte.d.ts +7 -0
- package/dist/components/TextInputMilk/TextInputMilk.types.d.ts +7 -0
- package/dist/components/TextInputMilk/TextInputMilk.types.js +1 -0
- package/dist/components/TextInputMilk/index.d.ts +2 -0
- package/dist/components/TextInputMilk/index.js +2 -0
- package/dist/components/ThemeProvider/ThemeDefault.js +11 -6
- package/dist/components/ThemeProvider/ThemeProvider.types.d.ts +6 -0
- package/dist/components/index.d.ts +4 -1
- package/dist/components/index.js +4 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,39 +46,41 @@
|
|
|
46
46
|
--color: var(--text-base-main);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
.Button.Button-
|
|
50
|
-
--padding-y:
|
|
51
|
-
--padding-x:
|
|
52
|
-
--font-size:
|
|
53
|
-
--line-height:
|
|
54
|
-
--border-radius:
|
|
55
|
-
--inner-gap:
|
|
49
|
+
.Button.Button-sm[data-variant="milk"] {
|
|
50
|
+
--padding-y: 5px;
|
|
51
|
+
--padding-x: 10px;
|
|
52
|
+
--font-size: var(--font-size-sm);
|
|
53
|
+
--line-height: var(--line-height-sm);
|
|
54
|
+
--border-radius: var(--border-radius-button-sm);
|
|
55
|
+
--inner-gap: 3px;
|
|
56
56
|
&.Button-icon {
|
|
57
|
-
--padding-y:
|
|
58
|
-
--padding-x:
|
|
57
|
+
--padding-y: 6px;
|
|
58
|
+
--padding-x: 6px;
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
|
+
|
|
61
62
|
.Button.Button-md[data-variant="milk"] {
|
|
62
63
|
--padding-y: 5px;
|
|
63
64
|
--padding-x: 16px;
|
|
64
|
-
--font-size:
|
|
65
|
-
--line-height:
|
|
66
|
-
--border-radius:
|
|
65
|
+
--font-size: var(--font-size-md);
|
|
66
|
+
--line-height: var(--line-height-md);
|
|
67
|
+
--border-radius: var(--border-radius-button);
|
|
67
68
|
--inner-gap: 5px;
|
|
68
69
|
&.Button-icon {
|
|
69
70
|
--padding-y: 8px;
|
|
70
71
|
--padding-x: 8px;
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
--padding-
|
|
76
|
-
--
|
|
77
|
-
--
|
|
78
|
-
--
|
|
79
|
-
--
|
|
74
|
+
|
|
75
|
+
.Button.Button-lg[data-variant="milk"] {
|
|
76
|
+
--padding-y: 7px;
|
|
77
|
+
--padding-x: 20px;
|
|
78
|
+
--font-size: var(--font-size-lg);
|
|
79
|
+
--line-height: var(--line-height-lg);
|
|
80
|
+
--border-radius: var(--border-radius-button);
|
|
81
|
+
--inner-gap: 6px;
|
|
80
82
|
&.Button-icon {
|
|
81
|
-
--padding-y:
|
|
82
|
-
--padding-x:
|
|
83
|
+
--padding-y: 11px;
|
|
84
|
+
--padding-x: 11px;
|
|
83
85
|
}
|
|
84
86
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import { type Writable } from 'svelte/store';
|
|
3
|
+
export interface ICommandRootProps {
|
|
4
|
+
classNames?: string;
|
|
5
|
+
children: Snippet;
|
|
6
|
+
maxHeight?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface ICommandListProps {
|
|
9
|
+
children: Snippet;
|
|
10
|
+
}
|
|
11
|
+
export interface ICommandGroupProps {
|
|
12
|
+
heading?: string;
|
|
13
|
+
children: Snippet;
|
|
14
|
+
}
|
|
15
|
+
export interface ICommandItemProps {
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
onClick?: (e: MouseEvent | null) => void;
|
|
18
|
+
children: Snippet;
|
|
19
|
+
}
|
|
20
|
+
export type CommandItemEntry = {
|
|
21
|
+
el: HTMLElement;
|
|
22
|
+
id: symbol;
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
};
|
|
25
|
+
export interface ICommandItems {
|
|
26
|
+
items: Writable<CommandItemEntry[]>;
|
|
27
|
+
activeItemId: Writable<symbol>;
|
|
28
|
+
searchQuery: Writable<string>;
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import {} from 'svelte/store';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CommandItemEntry } from './Command.types';
|
|
2
|
+
export declare function sortItemsByDOMPosition(list: {
|
|
3
|
+
el: HTMLElement;
|
|
4
|
+
id: symbol;
|
|
5
|
+
}[]): {
|
|
6
|
+
el: HTMLElement;
|
|
7
|
+
id: symbol;
|
|
8
|
+
}[];
|
|
9
|
+
export declare function addItemSorted(list: CommandItemEntry[], entry: CommandItemEntry): CommandItemEntry[];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export function sortItemsByDOMPosition(list) {
|
|
2
|
+
return list.slice().sort((a, b) => a.el.compareDocumentPosition(b.el) & Node.DOCUMENT_POSITION_FOLLOWING
|
|
3
|
+
? -1
|
|
4
|
+
: 1);
|
|
5
|
+
}
|
|
6
|
+
export function addItemSorted(list, entry) {
|
|
7
|
+
return sortItemsByDOMPosition([...list, entry]);
|
|
8
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { setContext } from 'svelte';
|
|
3
|
+
import { writable } from 'svelte/store';
|
|
4
|
+
import type { ICommandGroupProps } from './Command.types';
|
|
5
|
+
let { heading, children } : ICommandGroupProps = $props();
|
|
6
|
+
|
|
7
|
+
let commandGroupItemsVisibility = writable<Record<symbol, boolean>>({});
|
|
8
|
+
setContext('command-group-visibility', { commandGroupItemsVisibility });
|
|
9
|
+
let allFalse = $derived(Object.getOwnPropertySymbols($commandGroupItemsVisibility)
|
|
10
|
+
.every(sym => $commandGroupItemsVisibility[sym] === false));
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
<div class="CommandGroup" class:hidden={allFalse} >
|
|
19
|
+
{#if heading}
|
|
20
|
+
<div class="CommandGroup-heading">{heading}</div>
|
|
21
|
+
{/if}
|
|
22
|
+
{@render children()}
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
<style>.CommandGroup {
|
|
28
|
+
padding: var(--group-padding-y) var(--group-padding-x);
|
|
29
|
+
border-bottom: 1px solid var(--border-color);
|
|
30
|
+
}
|
|
31
|
+
.CommandGroup.hidden {
|
|
32
|
+
display: none;
|
|
33
|
+
}
|
|
34
|
+
.CommandGroup:last-of-type {
|
|
35
|
+
border-bottom: none;
|
|
36
|
+
padding: 0 var(--group-padding-x);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.CommandGroup .CommandGroup-heading {
|
|
40
|
+
padding: var(--padding-y) var(--padding-x);
|
|
41
|
+
color: var(--group-heading-color);
|
|
42
|
+
font-size: 0.875em;
|
|
43
|
+
}</style>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { TextInputBlock } from '../..';
|
|
3
|
+
import type { ITextInputBlockProps } from '../TextInputBlock/TextInputBlock.types';
|
|
4
|
+
import { Search2LineSystem } from 'svelte-remix';
|
|
5
|
+
import { getContext } from 'svelte';
|
|
6
|
+
import { type Writable } from 'svelte/store';
|
|
7
|
+
|
|
8
|
+
const { searchQuery } = getContext<{ searchQuery: Writable<string> }>('command-items');
|
|
9
|
+
|
|
10
|
+
let value = $state('');
|
|
11
|
+
|
|
12
|
+
function onInput(e: Event) {
|
|
13
|
+
searchQuery.set(value.toLowerCase().trim());
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface ICommandInputProps extends ITextInputBlockProps {
|
|
17
|
+
autoFocus?: boolean;
|
|
18
|
+
visible?: boolean;
|
|
19
|
+
placeholder?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let { autoFocus, visible=true, placeholder, ...rest }: ICommandInputProps = $props();
|
|
23
|
+
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
<div class={`CommandInput ${visible ? 'visible' : ''}`}>
|
|
28
|
+
<TextInputBlock
|
|
29
|
+
readonly={!visible}
|
|
30
|
+
{autoFocus}
|
|
31
|
+
size='lg'
|
|
32
|
+
{placeholder}
|
|
33
|
+
variant="text"
|
|
34
|
+
onKeyUp={onInput}
|
|
35
|
+
bind:value type="text"
|
|
36
|
+
{...rest}
|
|
37
|
+
>
|
|
38
|
+
{#snippet prefix()}
|
|
39
|
+
<Search2LineSystem size="1em"/>
|
|
40
|
+
{/snippet}
|
|
41
|
+
</TextInputBlock>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
<style>.CommandInput {
|
|
46
|
+
--border-color: var(--line-base);
|
|
47
|
+
margin-bottom: 0.25rem;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.CommandInput {
|
|
51
|
+
border-bottom: 1px solid var(--border-color);
|
|
52
|
+
opacity: 0;
|
|
53
|
+
position: absolute;
|
|
54
|
+
z-index: -1;
|
|
55
|
+
}
|
|
56
|
+
.CommandInput.visible {
|
|
57
|
+
opacity: 1;
|
|
58
|
+
position: relative;
|
|
59
|
+
z-index: 0;
|
|
60
|
+
}</style>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ITextInputBlockProps } from '../TextInputBlock/TextInputBlock.types';
|
|
2
|
+
interface ICommandInputProps extends ITextInputBlockProps {
|
|
3
|
+
autoFocus?: boolean;
|
|
4
|
+
visible?: boolean;
|
|
5
|
+
placeholder?: string;
|
|
6
|
+
}
|
|
7
|
+
declare const CommandInput: import("svelte").Component<ICommandInputProps, {}, "">;
|
|
8
|
+
type CommandInput = ReturnType<typeof CommandInput>;
|
|
9
|
+
export default CommandInput;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getContext, onMount, onDestroy } from 'svelte';
|
|
3
|
+
import { derived, type Writable } from 'svelte/store';
|
|
4
|
+
import type { ICommandItemProps, ICommandItems } from './Command.types';
|
|
5
|
+
import { addItemSorted } from './Command.utils';
|
|
6
|
+
|
|
7
|
+
let { disabled, onClick, children } : ICommandItemProps = $props();
|
|
8
|
+
let el: HTMLElement;
|
|
9
|
+
const itemId = Symbol();
|
|
10
|
+
let textContent = $state('');
|
|
11
|
+
let shouldShow = $state(true);
|
|
12
|
+
const { items, activeItemId, searchQuery }: ICommandItems = getContext('command-items');
|
|
13
|
+
const { commandGroupItemsVisibility }: { commandGroupItemsVisibility: Writable<Record<symbol, boolean>>} = getContext('command-group-visibility');
|
|
14
|
+
|
|
15
|
+
// Определяем нужно ли отображать элемент согласно searchQueryshad
|
|
16
|
+
$effect(() => {
|
|
17
|
+
textContent = el?.textContent?.toLowerCase().trim() || '';
|
|
18
|
+
shouldShow = !$searchQuery || textContent.includes($searchQuery);
|
|
19
|
+
// Этот блок передает в группу объект со значениями [id]: visible (для каждого CommandItem)
|
|
20
|
+
commandGroupItemsVisibility.update((recs) => ({ ...recs, [itemId]: shouldShow }));
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
onMount(() => {
|
|
24
|
+
// Добавляем элемент в Root state
|
|
25
|
+
items.update(list => addItemSorted(list, { el, id: itemId, disabled: !!disabled }));
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
onDestroy(() => {
|
|
29
|
+
// Непонятно нахрена это здесь. Это обновляет Root state зачем-то
|
|
30
|
+
items.update(list => list.filter(item => item.id !== itemId));
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Вычисляем активный это элемент или нет
|
|
34
|
+
const isActive = derived(activeItemId, $itemId => $itemId === itemId);
|
|
35
|
+
|
|
36
|
+
function handleClick(e: MouseEvent) {
|
|
37
|
+
if (disabled) return;
|
|
38
|
+
activeItemId.set(itemId);
|
|
39
|
+
onClick?.(e);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Это не тестировалось еще TODO: to test
|
|
43
|
+
$effect(() => {
|
|
44
|
+
if ($isActive) {
|
|
45
|
+
el.scrollIntoView({ block: 'nearest' });
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
50
|
+
let key = (e.key);
|
|
51
|
+
if (key === 'Enter') {
|
|
52
|
+
onClick?.(null);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<div
|
|
59
|
+
bind:this={el}
|
|
60
|
+
onclick={handleClick}
|
|
61
|
+
onkeydown={handleKeyDown}
|
|
62
|
+
class={`CommandItem ${disabled ? 'disabled' : ''}`}
|
|
63
|
+
class:active={$isActive}
|
|
64
|
+
class:hidden={!shouldShow}
|
|
65
|
+
role="option"
|
|
66
|
+
tabindex="-1"
|
|
67
|
+
aria-selected={$isActive}
|
|
68
|
+
data-command-item
|
|
69
|
+
data-selected
|
|
70
|
+
>
|
|
71
|
+
{@render children()}
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<style>.CommandItem {
|
|
75
|
+
display: flex;
|
|
76
|
+
flex-direction: row;
|
|
77
|
+
align-items: center;
|
|
78
|
+
padding: var(--padding-y) var(--padding-x);
|
|
79
|
+
gap: var(--inner-gap);
|
|
80
|
+
cursor: pointer;
|
|
81
|
+
border-radius: var(--item-border-radius);
|
|
82
|
+
transition: background 0.3s ease-in-out;
|
|
83
|
+
}
|
|
84
|
+
.CommandItem.hidden {
|
|
85
|
+
display: none;
|
|
86
|
+
}
|
|
87
|
+
.CommandItem:focus-visible, .CommandItem:focus-within {
|
|
88
|
+
outline: none;
|
|
89
|
+
}
|
|
90
|
+
.CommandItem.active {
|
|
91
|
+
background: var(--item-active);
|
|
92
|
+
}
|
|
93
|
+
.CommandItem.active:not(.disabled):hover {
|
|
94
|
+
background: var(--item-active);
|
|
95
|
+
}
|
|
96
|
+
.CommandItem.disabled {
|
|
97
|
+
opacity: var(--opacity-item-disabled);
|
|
98
|
+
cursor: not-allowed;
|
|
99
|
+
}
|
|
100
|
+
.CommandItem:not(.disabled):hover {
|
|
101
|
+
background: var(--item-hover);
|
|
102
|
+
}</style>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ICommandListProps } from './Command.types';
|
|
3
|
+
let { children } : ICommandListProps = $props();
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<div class="CommandList" role="listbox">
|
|
7
|
+
{@render children()}
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<style>.CommandList {
|
|
11
|
+
display: flex;
|
|
12
|
+
flex-direction: column;
|
|
13
|
+
}</style>
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { setContext } from 'svelte';
|
|
3
|
+
import { writable, get } from 'svelte/store';
|
|
4
|
+
import type { ICommandRootProps, CommandItemEntry, ICommandItems } from './Command.types';
|
|
5
|
+
|
|
6
|
+
let { classNames, maxHeight, children }: ICommandRootProps = $props();
|
|
7
|
+
|
|
8
|
+
let rootEl: HTMLDivElement;
|
|
9
|
+
const items = writable<CommandItemEntry[]>([]);
|
|
10
|
+
let visibleItems = $derived($items.filter(i => {
|
|
11
|
+
const text = i.el.textContent?.toLowerCase() || '';
|
|
12
|
+
return text.includes($searchQuery);
|
|
13
|
+
}));
|
|
14
|
+
const activeItemId = writable<symbol>();
|
|
15
|
+
const searchQuery = writable('');
|
|
16
|
+
setContext<ICommandItems>('command-items', {
|
|
17
|
+
items, // список всех зарегистрированных children CommandItem
|
|
18
|
+
activeItemId, // ID текущего выбранного
|
|
19
|
+
searchQuery // строка поиска
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
function handleKeydown(e: KeyboardEvent) {
|
|
23
|
+
|
|
24
|
+
if (!visibleItems.length) return;
|
|
25
|
+
|
|
26
|
+
const index = visibleItems.findIndex(i => i.id === get(activeItemId));
|
|
27
|
+
const currentIndex = index === -1 ? 0 : index;
|
|
28
|
+
|
|
29
|
+
const findNext = (start: number) => {
|
|
30
|
+
for (let i = start + 1; i < visibleItems.length; i++) {
|
|
31
|
+
if (!visibleItems[i].disabled) return i;
|
|
32
|
+
}
|
|
33
|
+
return currentIndex;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const findPrev = (start: number) => {
|
|
37
|
+
for (let i = start - 1; i >= 0; i--) {
|
|
38
|
+
if (!visibleItems[i].disabled) return i;
|
|
39
|
+
}
|
|
40
|
+
return currentIndex;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
if (e.key === 'ArrowDown') {
|
|
44
|
+
e.preventDefault();
|
|
45
|
+
const next = findNext(currentIndex);
|
|
46
|
+
activeItemId.set(visibleItems[next].id);
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (e.key === 'ArrowUp') {
|
|
51
|
+
e.preventDefault();
|
|
52
|
+
const prev = findPrev(currentIndex);
|
|
53
|
+
activeItemId.set(visibleItems[prev].id);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (e.key === 'Enter') {
|
|
57
|
+
const id = get(activeItemId);
|
|
58
|
+
const current = get(items).find(item => item.id === id);
|
|
59
|
+
if (current && current.el && !current.disabled) {
|
|
60
|
+
current.el.click(); // Имитируем клик по активному CommandItem
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<div
|
|
68
|
+
class={`CommandRoot ${classNames}`}
|
|
69
|
+
bind:this={rootEl}
|
|
70
|
+
tabindex="0"
|
|
71
|
+
onkeydown={handleKeydown}
|
|
72
|
+
role="listbox"
|
|
73
|
+
style={`${maxHeight ? `max-height: ${maxHeight}px; overflow-y: auto;` : ''}`}
|
|
74
|
+
>
|
|
75
|
+
{@render children()}
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<style>.CommandRoot {
|
|
79
|
+
--border-color: var(--line-base);
|
|
80
|
+
--border-color-focus: var(--line-control-100);
|
|
81
|
+
--background-color: var(--bg-base);
|
|
82
|
+
--outter-padding: 4px;
|
|
83
|
+
--border-radius: var(--border-radius-panel);
|
|
84
|
+
--font-size: 1em;
|
|
85
|
+
--padding-y: 7px;
|
|
86
|
+
--padding-x: 12px;
|
|
87
|
+
--inner-gap: 7px;
|
|
88
|
+
--item-hover: var(--bg-base-100);
|
|
89
|
+
--item-active: var(--bg-base-200);
|
|
90
|
+
--item-border-radius: var(--border-radius-button);
|
|
91
|
+
--opacity-item-disabled: 0.6;
|
|
92
|
+
--group-heading-color: var(--text-base-muted);
|
|
93
|
+
--group-padding-y: 0;
|
|
94
|
+
--group-padding-x: 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.CommandRoot {
|
|
98
|
+
padding: var(--outter-padding);
|
|
99
|
+
border: 1px solid var(--border-color);
|
|
100
|
+
border-radius: var(--border-radius);
|
|
101
|
+
font-size: var(--font-size);
|
|
102
|
+
background: var(--background-color);
|
|
103
|
+
}
|
|
104
|
+
.CommandRoot:focus-visible, .CommandRoot:focus-within {
|
|
105
|
+
outline: none;
|
|
106
|
+
border-color: var(--line-control-100);
|
|
107
|
+
}</style>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { default as CommandRoot } from './CommandRoot.svelte';
|
|
2
|
+
export { default as CommandInput } from './CommandInput.svelte';
|
|
3
|
+
export { default as CommandList } from './CommandList.svelte';
|
|
4
|
+
export { default as CommandGroup } from './CommandGroup.svelte';
|
|
5
|
+
export { default as CommandItem } from './CommandItem.svelte';
|
|
6
|
+
export * from './Command.types';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { default as CommandRoot } from './CommandRoot.svelte';
|
|
2
|
+
export { default as CommandInput } from './CommandInput.svelte';
|
|
3
|
+
export { default as CommandList } from './CommandList.svelte';
|
|
4
|
+
export { default as CommandGroup } from './CommandGroup.svelte';
|
|
5
|
+
export { default as CommandItem } from './CommandItem.svelte';
|
|
6
|
+
export * from './Command.types';
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { TextInputMilk, type TextInputInstance } from '../..';
|
|
3
|
+
import type { ITextInputBlockProps } from './TextInputBlock.types';
|
|
4
|
+
import { CloseCircleFillSystem } from 'svelte-remix';
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
prefix,
|
|
8
|
+
suffix,
|
|
9
|
+
disabled,
|
|
10
|
+
value = $bindable(),
|
|
11
|
+
placeholder,
|
|
12
|
+
variant = 'contained',
|
|
13
|
+
onClear,
|
|
14
|
+
pseudoFocus,
|
|
15
|
+
...rest
|
|
16
|
+
}: ITextInputBlockProps = $props();
|
|
17
|
+
|
|
18
|
+
// Calculate suffix and prefix width
|
|
19
|
+
let prefixEl = $state<HTMLDivElement | null>(null);
|
|
20
|
+
let prefixWidth = $derived(prefixEl?.getBoundingClientRect().width || 0);
|
|
21
|
+
|
|
22
|
+
let suffixEl = $state<HTMLDivElement | null>(null);
|
|
23
|
+
let suffixWidth = $derived<number>(suffixEl?.getBoundingClientRect().width || 0);
|
|
24
|
+
|
|
25
|
+
// Прокидываем фокус примитива
|
|
26
|
+
let textInputRef: TextInputInstance;
|
|
27
|
+
export const focus = () => textInputRef.focus();
|
|
28
|
+
|
|
29
|
+
const handleClear = () => {
|
|
30
|
+
value = "";
|
|
31
|
+
textInputRef.focus();
|
|
32
|
+
onClear?.();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<div class="TextInputBlock">
|
|
38
|
+
{#if prefix}
|
|
39
|
+
<div class="prefix" bind:this={prefixEl}>
|
|
40
|
+
{@render prefix()}
|
|
41
|
+
</div>
|
|
42
|
+
{/if}
|
|
43
|
+
|
|
44
|
+
<TextInputMilk
|
|
45
|
+
placeholder={prefix ? (prefixWidth !== 0 ? placeholder : '') : placeholder}
|
|
46
|
+
{variant}
|
|
47
|
+
{disabled}
|
|
48
|
+
bind:this={textInputRef}
|
|
49
|
+
style={`
|
|
50
|
+
${prefix ? `padding-left: ${prefixWidth + 4}px; padding-right: ${suffixWidth + 4}px;` : ``}
|
|
51
|
+
${pseudoFocus ? `border-color: var(--border-color-focus)` : ``}
|
|
52
|
+
`}
|
|
53
|
+
bind:value
|
|
54
|
+
{...rest}
|
|
55
|
+
/>
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
{#if onClear && !disabled && value !== ''}
|
|
59
|
+
<!-- I deliberately excluded this button from tabindex -->
|
|
60
|
+
<button tabindex="-1" type="button" class="clear-value" style={`right: ${suffixWidth + 12}px;`} onmouseup={handleClear}>
|
|
61
|
+
<CloseCircleFillSystem size="1em" />
|
|
62
|
+
</button>
|
|
63
|
+
{/if}
|
|
64
|
+
|
|
65
|
+
{#if suffix}
|
|
66
|
+
<div class="suffix" bind:this={suffixEl}>
|
|
67
|
+
{@render suffix()}
|
|
68
|
+
</div>
|
|
69
|
+
{/if}
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<style>.TextInputBlock {
|
|
73
|
+
position: relative;
|
|
74
|
+
display: flex;
|
|
75
|
+
width: 100%;
|
|
76
|
+
}
|
|
77
|
+
.TextInputBlock .prefix, .TextInputBlock .suffix {
|
|
78
|
+
position: absolute;
|
|
79
|
+
z-index: 1;
|
|
80
|
+
top: 0;
|
|
81
|
+
bottom: 0;
|
|
82
|
+
height: 100%;
|
|
83
|
+
display: flex;
|
|
84
|
+
align-items: center;
|
|
85
|
+
}
|
|
86
|
+
.TextInputBlock .prefix {
|
|
87
|
+
left: 0;
|
|
88
|
+
padding-left: 0.75em;
|
|
89
|
+
}
|
|
90
|
+
.TextInputBlock .suffix {
|
|
91
|
+
right: 0;
|
|
92
|
+
padding-right: 0.75em;
|
|
93
|
+
}
|
|
94
|
+
.TextInputBlock .clear-value {
|
|
95
|
+
position: absolute;
|
|
96
|
+
z-index: 1;
|
|
97
|
+
top: 4px;
|
|
98
|
+
bottom: 4px;
|
|
99
|
+
height: calc(100% - 8px);
|
|
100
|
+
cursor: pointer;
|
|
101
|
+
border: 1px solid transparent;
|
|
102
|
+
border-radius: 8px;
|
|
103
|
+
padding: 0 4px;
|
|
104
|
+
}
|
|
105
|
+
.TextInputBlock .clear-value:focus {
|
|
106
|
+
outline: none;
|
|
107
|
+
background-color: #EDEEF0;
|
|
108
|
+
border-color: #D3D5DC;
|
|
109
|
+
}</style>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ITextInputBlockProps } from './TextInputBlock.types';
|
|
2
|
+
declare const TextInputBlock: import("svelte").Component<ITextInputBlockProps, {
|
|
3
|
+
focus: () => void;
|
|
4
|
+
}, "value">;
|
|
5
|
+
type TextInputBlock = ReturnType<typeof TextInputBlock>;
|
|
6
|
+
export default TextInputBlock;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
.TextInput.TextInput-sm {
|
|
2
|
+
--padding-y: 5px;
|
|
3
|
+
--padding-x: 5px;
|
|
4
|
+
--font-size: var(--font-size-sm);
|
|
5
|
+
--line-height: var(--line-height-sm);
|
|
6
|
+
--border-radius: var(--border-radius-button-sm);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.TextInput.TextInput-md {
|
|
10
|
+
--padding-y: 5px;
|
|
11
|
+
--padding-x: 6px;
|
|
12
|
+
--font-size: var(--font-size-md);
|
|
13
|
+
--line-height: var(--line-height-md);
|
|
14
|
+
--border-radius: var(--border-radius-button);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.TextInput.TextInput-lg {
|
|
18
|
+
--padding-y: 7px;
|
|
19
|
+
--padding-x: 12px;
|
|
20
|
+
--font-size: var(--font-size-lg);
|
|
21
|
+
--line-height: var(--line-height-lg);
|
|
22
|
+
--border-radius: var(--border-radius-button);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.TextInput.TextInput-text {
|
|
26
|
+
--padding-x: 0px;
|
|
27
|
+
--border-radius: 0;
|
|
28
|
+
--border-width: 0px;
|
|
29
|
+
--background-color: transparent;
|
|
30
|
+
--border-color: transparent;
|
|
31
|
+
--border-color-focus: transparent;
|
|
32
|
+
}
|
|
33
|
+
.TextInput.TextInput-underlined {
|
|
34
|
+
--padding-x: 0px;
|
|
35
|
+
|
|
36
|
+
--border-radius: 0;
|
|
37
|
+
--border-style: solid;
|
|
38
|
+
--border-width: var(--stroke-base);
|
|
39
|
+
--border-top-width: 0px;
|
|
40
|
+
--border-bottom-width: var(--border-width);
|
|
41
|
+
--border-left-width: 0px;
|
|
42
|
+
--border-right-width: 0px;
|
|
43
|
+
|
|
44
|
+
--background-color: #fff;
|
|
45
|
+
--border-color: #D3D5DC;
|
|
46
|
+
--border-color-focus: #151617;
|
|
47
|
+
|
|
48
|
+
--background-disabled: var(--background-color);
|
|
49
|
+
--border-color-disabled: var(--border-color);
|
|
50
|
+
--opacity-disabled: 0.6;
|
|
51
|
+
|
|
52
|
+
--border-style-disabled: dashed;
|
|
53
|
+
--border-width-disabled: var(--border-width);
|
|
54
|
+
--border-top-width-disabled: 0px;
|
|
55
|
+
--border-bottom-width-disabled: var(--border-width-disabled);
|
|
56
|
+
--border-left-width-disabled: 0px;
|
|
57
|
+
--border-right-width-disabled: 0px;
|
|
58
|
+
|
|
59
|
+
--background-readonly: var(--background-disabled);
|
|
60
|
+
--border-color-readonly: var(--border-color);
|
|
61
|
+
--opacity-readonly: var(--opacity-disabled);
|
|
62
|
+
|
|
63
|
+
--border-style-readonly: dashed;
|
|
64
|
+
--border-width-readonly: var(--border-width);
|
|
65
|
+
--border-top-width-readonly: 0px;
|
|
66
|
+
--border-bottom-width-readonly: var(--border-width);
|
|
67
|
+
--border-left-width-readonly: 0px;
|
|
68
|
+
--border-right-width-readonly: 0px;
|
|
69
|
+
|
|
70
|
+
--border-color-invalid: #EA6D60;
|
|
71
|
+
--background-invalid: var(--background-color);
|
|
72
|
+
--border-style-invalid: var(--border-style);
|
|
73
|
+
--border-width-invalid: var(--border-width);
|
|
74
|
+
--border-top-width-invalid: 0px;
|
|
75
|
+
--border-bottom-width-invalid: var(--border-width);
|
|
76
|
+
--border-left-width-invalid: 0px;
|
|
77
|
+
--border-right-width-invalid: 0px;
|
|
78
|
+
}
|
|
79
|
+
.TextInput.TextInput-contained {
|
|
80
|
+
// Default
|
|
81
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { TextInput, type TextInputInstance } from '../..';
|
|
3
|
+
import type { ITextInputMilkProps } from './TextInputMilk.types';
|
|
4
|
+
|
|
5
|
+
import "./TextInputMilk.scss";
|
|
6
|
+
|
|
7
|
+
let {
|
|
8
|
+
size = 'sm',
|
|
9
|
+
variant = 'contained',
|
|
10
|
+
value = $bindable(),
|
|
11
|
+
...rest
|
|
12
|
+
}: ITextInputMilkProps = $props();
|
|
13
|
+
|
|
14
|
+
// Прокидываем фокус примитива
|
|
15
|
+
let textInputRef: TextInputInstance;
|
|
16
|
+
export const focus = () => textInputRef.focus();
|
|
17
|
+
|
|
18
|
+
let sizeClassName =
|
|
19
|
+
size === "sm" ? "TextInput-sm"
|
|
20
|
+
: size === "md" ? "TextInput-md"
|
|
21
|
+
: size === "lg" ? "TextInput-lg"
|
|
22
|
+
: "";
|
|
23
|
+
|
|
24
|
+
let variantClassname =
|
|
25
|
+
variant === "text" ? "TextInput-text"
|
|
26
|
+
: variant === "underlined" ? "TextInput-underlined"
|
|
27
|
+
: variant === "contained" ? "TextInput-contained" : ""
|
|
28
|
+
|
|
29
|
+
let classNames = `${sizeClassName} ${variantClassname}`;
|
|
30
|
+
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
<TextInput
|
|
35
|
+
bind:value
|
|
36
|
+
bind:this={textInputRef}
|
|
37
|
+
{classNames}
|
|
38
|
+
{...rest}
|
|
39
|
+
/>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ITextInputMilkProps } from './TextInputMilk.types';
|
|
2
|
+
import "./TextInputMilk.scss";
|
|
3
|
+
declare const TextInputMilk: import("svelte").Component<ITextInputMilkProps, {
|
|
4
|
+
focus: () => void;
|
|
5
|
+
}, "value">;
|
|
6
|
+
type TextInputMilk = ReturnType<typeof TextInputMilk>;
|
|
7
|
+
export default TextInputMilk;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ITextInputProps } from '../TextInput/TextInput.types';
|
|
2
|
+
export type TextInputMilkSizes = 'sm' | 'md' | 'lg';
|
|
3
|
+
export type TextInputMilkVariants = 'underlined' | 'contained' | 'text';
|
|
4
|
+
export interface ITextInputMilkProps extends ITextInputProps {
|
|
5
|
+
size?: TextInputMilkSizes;
|
|
6
|
+
variant?: TextInputMilkVariants;
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -12,7 +12,6 @@ export const themeDefault = {
|
|
|
12
12
|
'--line-success': '#B6F7CB',
|
|
13
13
|
'--line-danger': '#FBC4BF',
|
|
14
14
|
'--line-secondary': '#BEB6F9',
|
|
15
|
-
// Add line-emp
|
|
16
15
|
'--bg-base': '#fff',
|
|
17
16
|
'--bg-base-100': '#EDEEF0',
|
|
18
17
|
'--bg-base-200': '#E2E4E8',
|
|
@@ -64,10 +63,10 @@ export const themeDefault = {
|
|
|
64
63
|
'--icon-primary-main': '#C29B09',
|
|
65
64
|
'--icon-success-main': '#077129',
|
|
66
65
|
'--icon-danger-main': '#C42A1A',
|
|
67
|
-
'--border-radius-button-sm': '
|
|
68
|
-
'--border-radius-button': '
|
|
69
|
-
'--border-radius-panel': '
|
|
70
|
-
'--border-radius-window': '
|
|
66
|
+
'--border-radius-button-sm': '5px',
|
|
67
|
+
'--border-radius-button': '8px',
|
|
68
|
+
'--border-radius-panel': '12px',
|
|
69
|
+
'--border-radius-window': '20px',
|
|
71
70
|
'--stroke-base': '1px',
|
|
72
71
|
'--stroke-button': 'var(--stroke-base)',
|
|
73
72
|
'--spacing-xs-1': '4',
|
|
@@ -86,5 +85,11 @@ export const themeDefault = {
|
|
|
86
85
|
'--zindex-modal': '1055',
|
|
87
86
|
'--zindex-popover': '1070',
|
|
88
87
|
'--zindex-tooltip': '1080',
|
|
89
|
-
'--zindex-toast': '1090'
|
|
88
|
+
'--zindex-toast': '1090',
|
|
89
|
+
'--font-size-sm': '12px',
|
|
90
|
+
'--line-height-sm': '14px',
|
|
91
|
+
'--font-size-md': '14px',
|
|
92
|
+
'--line-height-md': '20px',
|
|
93
|
+
'--font-size-lg': '16px',
|
|
94
|
+
'--line-height-lg': '24px',
|
|
90
95
|
};
|
|
@@ -87,4 +87,10 @@ export interface ITheme {
|
|
|
87
87
|
'--zindex-popover': string;
|
|
88
88
|
'--zindex-tooltip': string;
|
|
89
89
|
'--zindex-toast': string;
|
|
90
|
+
'--font-size-sm': string;
|
|
91
|
+
'--line-height-sm': string;
|
|
92
|
+
'--font-size-md': string;
|
|
93
|
+
'--line-height-md': string;
|
|
94
|
+
'--font-size-lg': string;
|
|
95
|
+
'--line-height-lg': string;
|
|
90
96
|
}
|
|
@@ -6,5 +6,8 @@ export * from './ThemeProvider';
|
|
|
6
6
|
export * from './Button';
|
|
7
7
|
export * from './ButtonMilk';
|
|
8
8
|
export * from './Badge';
|
|
9
|
-
export * from './TextInput';
|
|
10
9
|
export * from './Label';
|
|
10
|
+
export * from './TextInput';
|
|
11
|
+
export * from './TextInputMilk';
|
|
12
|
+
export * from './TextInputBlock';
|
|
13
|
+
export * from './Command';
|
package/dist/components/index.js
CHANGED
|
@@ -6,5 +6,8 @@ export * from './ThemeProvider';
|
|
|
6
6
|
export * from './Button';
|
|
7
7
|
export * from './ButtonMilk';
|
|
8
8
|
export * from './Badge';
|
|
9
|
-
export * from './TextInput';
|
|
10
9
|
export * from './Label';
|
|
10
|
+
export * from './TextInput';
|
|
11
|
+
export * from './TextInputMilk';
|
|
12
|
+
export * from './TextInputBlock';
|
|
13
|
+
export * from './Command';
|