@300codes/design-system 1.0.0 → 1.2.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/package.json +6 -3
- package/src/components/FlatIconButton/FlatIconButton.stories.ts +81 -0
- package/src/components/FlatIconButton/FlatIconButton.vue +57 -0
- package/src/components/FlatIconButton/index.ts +2 -0
- package/src/components/QuantitySelector/QuantitySelector.stories.ts +84 -0
- package/src/components/QuantitySelector/QuantitySelector.vue +142 -0
- package/src/components/QuantitySelector/index.ts +2 -0
- package/src/components/SelectInput/SelectInput.vue +3 -3
- package/src/components/TextInput/TextInput.vue +5 -5
- package/src/components/index.ts +2 -0
- package/src/css/tokens.css +34 -9
- package/src/components/TextInput/components/InputIconButton.vue +0 -54
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@300codes/design-system",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"src/components",
|
|
@@ -21,7 +21,9 @@
|
|
|
21
21
|
"./radio-input": "./src/components/RadioInput/index.ts",
|
|
22
22
|
"./tabs-list": "./src/components/TabsList/index.ts",
|
|
23
23
|
"./search-input": "./src/components/SearchInput/index.ts",
|
|
24
|
-
"./base-tooltip": "./src/components/BaseTooltip/index.ts"
|
|
24
|
+
"./base-tooltip": "./src/components/BaseTooltip/index.ts",
|
|
25
|
+
"./flat-icon-button": "./src/components/FlatIconButton/index.ts",
|
|
26
|
+
"./quantity-selector": "./src/components/QuantitySelector/index.ts"
|
|
25
27
|
},
|
|
26
28
|
"scripts": {
|
|
27
29
|
"build": "vue-tsc --noEmit",
|
|
@@ -30,7 +32,8 @@
|
|
|
30
32
|
"lint": "eslint .",
|
|
31
33
|
"lint:fix": "eslint . --fix",
|
|
32
34
|
"format": "prettier --write .",
|
|
33
|
-
"type-check": "vue-tsc --noEmit"
|
|
35
|
+
"type-check": "vue-tsc --noEmit",
|
|
36
|
+
"release": "sh -c 'npm version $1 && git push && git push --tags && npm publish --access public' --"
|
|
34
37
|
},
|
|
35
38
|
"peerDependencies": {
|
|
36
39
|
"vue": "^3.5.0"
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
|
2
|
+
import type { ConcreteComponent } from 'vue';
|
|
3
|
+
import type { FlatIconButtonProps } from './FlatIconButton.vue';
|
|
4
|
+
import FlatIconButton from './FlatIconButton.vue';
|
|
5
|
+
|
|
6
|
+
const meta: Meta<FlatIconButtonProps> = {
|
|
7
|
+
title: 'Components/FlatIconButton',
|
|
8
|
+
component: FlatIconButton as unknown as ConcreteComponent<FlatIconButtonProps>,
|
|
9
|
+
tags: ['autodocs'],
|
|
10
|
+
argTypes: {
|
|
11
|
+
iconName: { control: 'text' },
|
|
12
|
+
iconPath: { control: 'text' },
|
|
13
|
+
iconSize: {
|
|
14
|
+
control: 'select',
|
|
15
|
+
options: ['2xs', 'xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', 'auto'],
|
|
16
|
+
},
|
|
17
|
+
label: { control: 'text' },
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default meta;
|
|
22
|
+
type Story = StoryObj<FlatIconButtonProps>;
|
|
23
|
+
|
|
24
|
+
export const Default: Story = {
|
|
25
|
+
args: {
|
|
26
|
+
iconName: 'close',
|
|
27
|
+
iconSize: 'sm',
|
|
28
|
+
label: 'Close',
|
|
29
|
+
},
|
|
30
|
+
render: (args: FlatIconButtonProps) => ({
|
|
31
|
+
components: { FlatIconButton },
|
|
32
|
+
setup() {
|
|
33
|
+
return { args };
|
|
34
|
+
},
|
|
35
|
+
template: '<FlatIconButton v-bind="args" />',
|
|
36
|
+
}),
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const Search: Story = {
|
|
40
|
+
args: {
|
|
41
|
+
iconName: 'search',
|
|
42
|
+
iconSize: 'sm',
|
|
43
|
+
label: 'Search',
|
|
44
|
+
},
|
|
45
|
+
render: (args: FlatIconButtonProps) => ({
|
|
46
|
+
components: { FlatIconButton },
|
|
47
|
+
setup() {
|
|
48
|
+
return { args };
|
|
49
|
+
},
|
|
50
|
+
template: '<FlatIconButton v-bind="args" />',
|
|
51
|
+
}),
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const MediumSize: Story = {
|
|
55
|
+
args: {
|
|
56
|
+
iconName: 'close',
|
|
57
|
+
iconSize: 'md',
|
|
58
|
+
label: 'Close',
|
|
59
|
+
},
|
|
60
|
+
render: (args: FlatIconButtonProps) => ({
|
|
61
|
+
components: { FlatIconButton },
|
|
62
|
+
setup() {
|
|
63
|
+
return { args };
|
|
64
|
+
},
|
|
65
|
+
template: '<FlatIconButton v-bind="args" />',
|
|
66
|
+
}),
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export const Sizes: Story = {
|
|
70
|
+
render: () => ({
|
|
71
|
+
components: { FlatIconButton },
|
|
72
|
+
template: `
|
|
73
|
+
<div class="flex items-center gap-4">
|
|
74
|
+
<FlatIconButton icon-name="close" icon-size="xs" aria-label="Close xs" />
|
|
75
|
+
<FlatIconButton icon-name="close" icon-size="sm" aria-label="Close sm" />
|
|
76
|
+
<FlatIconButton icon-name="close" icon-size="md" aria-label="Close md" />
|
|
77
|
+
<FlatIconButton icon-name="close" icon-size="lg" aria-label="Close lg" />
|
|
78
|
+
</div>
|
|
79
|
+
`,
|
|
80
|
+
}),
|
|
81
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { IconSize } from '../../types/icon';
|
|
3
|
+
import BaseIcon from '../BaseIcon/BaseIcon.vue';
|
|
4
|
+
|
|
5
|
+
export interface FlatIconButtonProps {
|
|
6
|
+
iconName: string;
|
|
7
|
+
iconPath?: string;
|
|
8
|
+
iconSize?: IconSize;
|
|
9
|
+
label: string;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
withDefaults(defineProps<FlatIconButtonProps>(), {
|
|
14
|
+
iconPath: '/icons',
|
|
15
|
+
iconSize: 'sm',
|
|
16
|
+
disabled: false,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const emit = defineEmits<{
|
|
20
|
+
click: [];
|
|
21
|
+
}>();
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template>
|
|
25
|
+
<button
|
|
26
|
+
type="button"
|
|
27
|
+
:class="['flatIconButton inline-flex items-center justify-center', disabled ? 'cursor-not-allowed' : 'cursor-pointer']"
|
|
28
|
+
:aria-label="label"
|
|
29
|
+
:disabled="disabled"
|
|
30
|
+
@click="emit('click')"
|
|
31
|
+
>
|
|
32
|
+
<BaseIcon
|
|
33
|
+
:name="iconName"
|
|
34
|
+
:icon-path="iconPath"
|
|
35
|
+
:size="iconSize"
|
|
36
|
+
/>
|
|
37
|
+
</button>
|
|
38
|
+
</template>
|
|
39
|
+
|
|
40
|
+
<style scoped>
|
|
41
|
+
.flatIconButton {
|
|
42
|
+
color: var(--flatIconButton-fg, #0e161b);
|
|
43
|
+
background: var(--flatIconButton-bg, transparent);
|
|
44
|
+
padding: var(--flatIconButton-p, 0.25rem);
|
|
45
|
+
border-radius: var(--flatIconButton-radius, 9999px);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.flatIconButton:hover:not(:disabled) {
|
|
49
|
+
background-color: var(--flatIconButton-bg-hover, #f3f4f6);
|
|
50
|
+
color: var(--flatIconButton-fg-hover, #0e161b);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.flatIconButton:focus-visible {
|
|
54
|
+
outline: 2px solid var(--flatIconButton-outline, #0066cc);
|
|
55
|
+
outline-offset: 1px;
|
|
56
|
+
}
|
|
57
|
+
</style>
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
|
2
|
+
import type { ConcreteComponent } from 'vue';
|
|
3
|
+
import { ref } from 'vue';
|
|
4
|
+
import type { QuantitySelectorProps } from './QuantitySelector.vue';
|
|
5
|
+
import QuantitySelector from './QuantitySelector.vue';
|
|
6
|
+
|
|
7
|
+
const meta: Meta<QuantitySelectorProps> = {
|
|
8
|
+
title: 'Components/QuantitySelector',
|
|
9
|
+
component: QuantitySelector as unknown as ConcreteComponent<QuantitySelectorProps>,
|
|
10
|
+
tags: ['autodocs'],
|
|
11
|
+
argTypes: {
|
|
12
|
+
size: { control: 'select', options: ['sm', 'md'] },
|
|
13
|
+
max: { control: 'number' },
|
|
14
|
+
disabled: { control: 'boolean' },
|
|
15
|
+
name: { control: 'text' },
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default meta;
|
|
20
|
+
type Story = StoryObj<QuantitySelectorProps>;
|
|
21
|
+
|
|
22
|
+
export const Default: Story = {
|
|
23
|
+
args: {
|
|
24
|
+
name: 'quantity',
|
|
25
|
+
size: 'md',
|
|
26
|
+
},
|
|
27
|
+
render: (args: QuantitySelectorProps) => ({
|
|
28
|
+
components: { QuantitySelector },
|
|
29
|
+
setup() {
|
|
30
|
+
const value = ref(1);
|
|
31
|
+
return { args, value };
|
|
32
|
+
},
|
|
33
|
+
template: '<QuantitySelector v-bind="args" v-model="value" />',
|
|
34
|
+
}),
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const Sizes: Story = {
|
|
38
|
+
render: () => ({
|
|
39
|
+
components: { QuantitySelector },
|
|
40
|
+
setup() {
|
|
41
|
+
const sm = ref(1);
|
|
42
|
+
const md = ref(1);
|
|
43
|
+
return { sm, md };
|
|
44
|
+
},
|
|
45
|
+
template: `
|
|
46
|
+
<div class="flex items-center gap-6">
|
|
47
|
+
<QuantitySelector name="qty-sm" size="sm" v-model="sm" />
|
|
48
|
+
<QuantitySelector name="qty-md" size="md" v-model="md" />
|
|
49
|
+
</div>
|
|
50
|
+
`,
|
|
51
|
+
}),
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const WithMax: Story = {
|
|
55
|
+
args: {
|
|
56
|
+
name: 'quantity-max',
|
|
57
|
+
size: 'md',
|
|
58
|
+
max: 5,
|
|
59
|
+
},
|
|
60
|
+
render: (args: QuantitySelectorProps) => ({
|
|
61
|
+
components: { QuantitySelector },
|
|
62
|
+
setup() {
|
|
63
|
+
const value = ref(3);
|
|
64
|
+
return { args, value };
|
|
65
|
+
},
|
|
66
|
+
template: '<QuantitySelector v-bind="args" v-model="value" />',
|
|
67
|
+
}),
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const Disabled: Story = {
|
|
71
|
+
args: {
|
|
72
|
+
name: 'quantity-disabled',
|
|
73
|
+
size: 'md',
|
|
74
|
+
disabled: true,
|
|
75
|
+
},
|
|
76
|
+
render: (args: QuantitySelectorProps) => ({
|
|
77
|
+
components: { QuantitySelector },
|
|
78
|
+
setup() {
|
|
79
|
+
const value = ref(1);
|
|
80
|
+
return { args, value };
|
|
81
|
+
},
|
|
82
|
+
template: '<QuantitySelector v-bind="args" v-model="value" />',
|
|
83
|
+
}),
|
|
84
|
+
};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, ref, watch } from 'vue';
|
|
3
|
+
import type { IconSize } from '../../types/icon';
|
|
4
|
+
import FlatIconButton from '../FlatIconButton/FlatIconButton.vue';
|
|
5
|
+
|
|
6
|
+
export interface QuantitySelectorProps {
|
|
7
|
+
name: string;
|
|
8
|
+
size?: 'sm' | 'md';
|
|
9
|
+
max?: number;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
inputLabel?: string;
|
|
12
|
+
decreaseLabel?: string;
|
|
13
|
+
increaseLabel?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const props = withDefaults(defineProps<QuantitySelectorProps>(), {
|
|
17
|
+
size: 'md',
|
|
18
|
+
max: undefined,
|
|
19
|
+
disabled: false,
|
|
20
|
+
inputLabel: 'Quantity',
|
|
21
|
+
decreaseLabel: 'Decrease',
|
|
22
|
+
increaseLabel: 'Increase',
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const model = defineModel<number>({ default: 1 });
|
|
26
|
+
|
|
27
|
+
const text = ref(String(model.value));
|
|
28
|
+
|
|
29
|
+
const isAtMin = computed(() => model.value <= 1);
|
|
30
|
+
const isAtMax = computed(() => props.max !== undefined && model.value >= props.max);
|
|
31
|
+
const iconSize = computed<IconSize>(() => (props.size === 'sm' ? 'md' : 'lg'));
|
|
32
|
+
|
|
33
|
+
const disabledBtnStyle = { '--flatIconButton-fg': 'var(--quantitySelector-fg-disabled, #89979f)' };
|
|
34
|
+
const disabledPropStyle = { '--flatIconButton-fg': 'transparent' };
|
|
35
|
+
|
|
36
|
+
function onDecrease() {
|
|
37
|
+
if (props.disabled || isAtMin.value) return;
|
|
38
|
+
model.value--;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function onIncrease() {
|
|
42
|
+
if (props.disabled || isAtMax.value) return;
|
|
43
|
+
model.value++;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function onInput(event: Event) {
|
|
47
|
+
const input = event.target as HTMLInputElement;
|
|
48
|
+
const filtered = input.value.replace(/\D/g, '');
|
|
49
|
+
input.value = filtered;
|
|
50
|
+
text.value = filtered;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function onBlur() {
|
|
54
|
+
const parsed = parseInt(text.value, 10);
|
|
55
|
+
let value = isNaN(parsed) || parsed < 1 ? 1 : parsed;
|
|
56
|
+
|
|
57
|
+
if (props.max !== undefined) value = Math.min(value, props.max);
|
|
58
|
+
|
|
59
|
+
model.value = value;
|
|
60
|
+
text.value = String(value);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
watch(model, (val) => {
|
|
64
|
+
text.value = String(val);
|
|
65
|
+
});
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<template>
|
|
69
|
+
<div
|
|
70
|
+
:class="[
|
|
71
|
+
'quantitySelector',
|
|
72
|
+
`quantitySelector--${size}`,
|
|
73
|
+
'inline-flex items-center overflow-hidden',
|
|
74
|
+
]"
|
|
75
|
+
>
|
|
76
|
+
<FlatIconButton
|
|
77
|
+
icon-name="minus"
|
|
78
|
+
:icon-size="iconSize"
|
|
79
|
+
:label="decreaseLabel"
|
|
80
|
+
:disabled="disabled || isAtMin"
|
|
81
|
+
class="h-full aspect-square"
|
|
82
|
+
:style="disabled ? disabledPropStyle : isAtMin ? disabledBtnStyle : undefined"
|
|
83
|
+
@click="onDecrease"
|
|
84
|
+
/>
|
|
85
|
+
|
|
86
|
+
<input
|
|
87
|
+
:name="name"
|
|
88
|
+
:value="disabled ? '1' : text"
|
|
89
|
+
type="text"
|
|
90
|
+
inputmode="numeric"
|
|
91
|
+
pattern="[0-9]*"
|
|
92
|
+
autocomplete="off"
|
|
93
|
+
:aria-label="inputLabel"
|
|
94
|
+
:disabled="disabled"
|
|
95
|
+
:class="[
|
|
96
|
+
'quantitySelector__input',
|
|
97
|
+
'bg-transparent border-0 outline-none text-center leading-normal h-full shrink-0 disabled:cursor-not-allowed',
|
|
98
|
+
]"
|
|
99
|
+
@input="onInput"
|
|
100
|
+
@blur="onBlur"
|
|
101
|
+
>
|
|
102
|
+
|
|
103
|
+
<FlatIconButton
|
|
104
|
+
icon-name="plus"
|
|
105
|
+
:icon-size="iconSize"
|
|
106
|
+
:label="increaseLabel"
|
|
107
|
+
:disabled="disabled || isAtMax"
|
|
108
|
+
class="h-full aspect-square"
|
|
109
|
+
:style="disabled ? disabledPropStyle : isAtMax ? disabledBtnStyle : undefined"
|
|
110
|
+
@click="onIncrease"
|
|
111
|
+
/>
|
|
112
|
+
</div>
|
|
113
|
+
</template>
|
|
114
|
+
|
|
115
|
+
<style scoped>
|
|
116
|
+
.quantitySelector {
|
|
117
|
+
--_h: var(--quantitySelector-h, 3.5rem);
|
|
118
|
+
--_input-w: var(--quantitySelector-input-w, 2.125rem);
|
|
119
|
+
--_fs: var(--quantitySelector-font-size, 1rem);
|
|
120
|
+
|
|
121
|
+
height: var(--_h);
|
|
122
|
+
background-color: var(--quantitySelector-bg, #f3f5f7);
|
|
123
|
+
border: var(--quantitySelector-border-width, 0) solid var(--quantitySelector-border, #d6dde1);
|
|
124
|
+
border-radius: var(--quantitySelector-radius, 9999px);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.quantitySelector--sm {
|
|
128
|
+
--_h: var(--quantitySelector-sm-h, 2.5rem);
|
|
129
|
+
--_input-w: var(--quantitySelector-sm-input-w, 2.875rem);
|
|
130
|
+
--_fs: var(--quantitySelector-sm-font-size, 0.875rem);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.quantitySelector__input {
|
|
134
|
+
width: var(--_input-w);
|
|
135
|
+
font-size: var(--_fs);
|
|
136
|
+
color: var(--quantitySelector-fg, #0e161b);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.quantitySelector__input:hover:not(:disabled) {
|
|
140
|
+
background-color: var(--quantitySelector-bg-hover, #f3f5f7);
|
|
141
|
+
}
|
|
142
|
+
</style>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { ref, computed, nextTick, watch } from 'vue';
|
|
3
3
|
import { useMediaQuery, onClickOutside } from '@vueuse/core';
|
|
4
4
|
import BaseIcon from '../BaseIcon/BaseIcon.vue';
|
|
5
|
-
import
|
|
5
|
+
import FlatIconButton from '../FlatIconButton/FlatIconButton.vue';
|
|
6
6
|
|
|
7
7
|
export type SelectOption = string | { value: string; label: string };
|
|
8
8
|
|
|
@@ -259,9 +259,9 @@ watch(isMobile, () => {
|
|
|
259
259
|
>
|
|
260
260
|
{{ mobileTitle || placeholder }}
|
|
261
261
|
|
|
262
|
-
<
|
|
262
|
+
<FlatIconButton
|
|
263
263
|
icon-name="close"
|
|
264
|
-
|
|
264
|
+
label="Close"
|
|
265
265
|
class="ml-auto"
|
|
266
266
|
@click="closeDropdown(true)"
|
|
267
267
|
/>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, ref, type Ref } from 'vue';
|
|
3
3
|
import type { IconSize } from '../../types/icon';
|
|
4
|
-
import
|
|
4
|
+
import FlatIconButton from '../FlatIconButton/FlatIconButton.vue';
|
|
5
5
|
|
|
6
6
|
export type InputType = 'text' | 'password' | 'email' | 'tel' | 'search';
|
|
7
7
|
export type FilterType = 'number' | 'number-dash' | 'alpha' | 'alpha-space';
|
|
@@ -124,22 +124,22 @@ defineExpose<{ el: Ref<HTMLInputElement | undefined> }>({ el });
|
|
|
124
124
|
@keyup.enter="emit('enter')"
|
|
125
125
|
>
|
|
126
126
|
|
|
127
|
-
<
|
|
127
|
+
<FlatIconButton
|
|
128
128
|
v-if="iconLeft"
|
|
129
129
|
:icon-name="iconLeft.name"
|
|
130
130
|
:icon-path="iconLeft.iconPath"
|
|
131
131
|
:icon-size="iconLeft.size"
|
|
132
|
-
:
|
|
132
|
+
:label="iconLeft.ariaLabel || iconLeft.name"
|
|
133
133
|
:class="['absolute top-1/2 -translate-y-1/2 left-3', iconLeft.class]"
|
|
134
134
|
@click="emit('clickIcon', 'left')"
|
|
135
135
|
/>
|
|
136
136
|
|
|
137
|
-
<
|
|
137
|
+
<FlatIconButton
|
|
138
138
|
v-if="iconRight"
|
|
139
139
|
:icon-name="iconRight.name"
|
|
140
140
|
:icon-path="iconRight.iconPath"
|
|
141
141
|
:icon-size="iconRight.size"
|
|
142
|
-
:
|
|
142
|
+
:label="iconRight.ariaLabel || iconRight.name"
|
|
143
143
|
:class="['absolute top-1/2 -translate-y-1/2 right-3', iconRight.class]"
|
|
144
144
|
@click="emit('clickIcon', 'right')"
|
|
145
145
|
/>
|
package/src/components/index.ts
CHANGED
|
@@ -12,4 +12,6 @@ export * from './RadioInput/index';
|
|
|
12
12
|
export * from './TabsList/index';
|
|
13
13
|
export * from './SearchInput/index';
|
|
14
14
|
export * from './BaseTooltip/index';
|
|
15
|
+
export * from './FlatIconButton/index';
|
|
16
|
+
export * from './QuantitySelector/index';
|
|
15
17
|
export type { IconSize } from '../types/icon';
|
package/src/css/tokens.css
CHANGED
|
@@ -182,15 +182,6 @@
|
|
|
182
182
|
--input-border-color-disabled: theme(--color-input-border-disabled);
|
|
183
183
|
--input-placeholder-fg-disabled: theme(--color-input-placeholder-disabled);
|
|
184
184
|
|
|
185
|
-
/* icon button */
|
|
186
|
-
--input-icon-bg: transparent;
|
|
187
|
-
--input-icon-fg: theme(--color-input-icon);
|
|
188
|
-
--input-icon-fg-hover: theme(--color-input-icon-hover);
|
|
189
|
-
--input-icon-bg-hover: theme(--color-input-icon-bg-hover);
|
|
190
|
-
--input-icon-outline: theme(--color-outline);
|
|
191
|
-
--input-icon-p: theme(--spacing-1);
|
|
192
|
-
--input-icon-radius: theme(--radius-full);
|
|
193
|
-
|
|
194
185
|
/* ────────────────────────────────────────────────
|
|
195
186
|
* TextInput — sizing
|
|
196
187
|
* ──────────────────────────────────────────────── */
|
|
@@ -382,6 +373,40 @@
|
|
|
382
373
|
--baseTooltip-lg-arrow-half-w: theme(--spacing-3); /* lg half-width 12px */
|
|
383
374
|
--baseTooltip-lg-arrow-h: theme(--spacing-4); /* lg arrow height 16px */
|
|
384
375
|
|
|
376
|
+
/* ────────────────────────────────────────────────
|
|
377
|
+
* FlatIconButton
|
|
378
|
+
* ──────────────────────────────────────────────── */
|
|
379
|
+
|
|
380
|
+
--flatIconButton-bg: transparent;
|
|
381
|
+
--flatIconButton-fg: theme(--color-input-icon);
|
|
382
|
+
--flatIconButton-fg-hover: theme(--color-input-icon-hover);
|
|
383
|
+
--flatIconButton-bg-hover: theme(--color-input-icon-bg-hover);
|
|
384
|
+
--flatIconButton-outline: theme(--color-outline);
|
|
385
|
+
--flatIconButton-p: theme(--spacing-1);
|
|
386
|
+
--flatIconButton-radius: theme(--radius-full);
|
|
387
|
+
|
|
388
|
+
/* ────────────────────────────────────────────────
|
|
389
|
+
* QuantitySelector
|
|
390
|
+
* ──────────────────────────────────────────────── */
|
|
391
|
+
|
|
392
|
+
--quantitySelector-bg: theme(--color-input-bg);
|
|
393
|
+
--quantitySelector-fg: theme(--color-input-fg);
|
|
394
|
+
--quantitySelector-bg-hover: #f3f5f7;
|
|
395
|
+
--quantitySelector-fg-disabled: theme(--color-disabled-foreground);
|
|
396
|
+
--quantitySelector-border: theme(--color-input-border);
|
|
397
|
+
--quantitySelector-border-width: 0;
|
|
398
|
+
--quantitySelector-radius: 9999px;
|
|
399
|
+
|
|
400
|
+
/* md (default) */
|
|
401
|
+
--quantitySelector-h: theme(--spacing-14); /* 56px */
|
|
402
|
+
--quantitySelector-input-w: theme(--spacing-12); /* 48px */
|
|
403
|
+
--quantitySelector-font-size: theme(--text-base); /* 16px */
|
|
404
|
+
|
|
405
|
+
/* sm */
|
|
406
|
+
--quantitySelector-sm-h: theme(--spacing-10); /* 40px */
|
|
407
|
+
--quantitySelector-sm-input-w: theme(--spacing-10); /* 40px */
|
|
408
|
+
--quantitySelector-sm-font-size: theme(--text-sm); /* 14px */
|
|
409
|
+
|
|
385
410
|
/* ────────────────────────────────────────────────
|
|
386
411
|
* TabsList
|
|
387
412
|
* ──────────────────────────────────────────────── */
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import type { IconSize } from '../../../types/icon';
|
|
3
|
-
import BaseIcon from '../../BaseIcon/BaseIcon.vue';
|
|
4
|
-
|
|
5
|
-
export interface InputIconButtonProps {
|
|
6
|
-
iconName: string;
|
|
7
|
-
iconPath?: string;
|
|
8
|
-
iconSize?: IconSize;
|
|
9
|
-
ariaLabel?: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
withDefaults(defineProps<InputIconButtonProps>(), {
|
|
13
|
-
iconPath: '/icons',
|
|
14
|
-
iconSize: 'sm',
|
|
15
|
-
ariaLabel: undefined,
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
const emit = defineEmits<{
|
|
19
|
-
click: [];
|
|
20
|
-
}>();
|
|
21
|
-
</script>
|
|
22
|
-
|
|
23
|
-
<template>
|
|
24
|
-
<button
|
|
25
|
-
type="button"
|
|
26
|
-
class="inputIconButton inline-flex items-center justify-center cursor-pointer"
|
|
27
|
-
:aria-label="ariaLabel"
|
|
28
|
-
@click="emit('click')"
|
|
29
|
-
>
|
|
30
|
-
<BaseIcon
|
|
31
|
-
:name="iconName"
|
|
32
|
-
:icon-path="iconPath"
|
|
33
|
-
:size="iconSize"
|
|
34
|
-
/>
|
|
35
|
-
</button>
|
|
36
|
-
</template>
|
|
37
|
-
|
|
38
|
-
<style scoped>
|
|
39
|
-
.inputIconButton {
|
|
40
|
-
color: var(--input-icon-fg, #0e161b);
|
|
41
|
-
background: var(--input-icon-bg, transparent);
|
|
42
|
-
padding: var(--input-icon-p, 0.25rem);
|
|
43
|
-
border-radius: var(--input-icon-radius, 9999px);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
.inputIconButton:hover {
|
|
47
|
-
background-color: var(--input-icon-bg-hover, #f3f4f6);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
.inputIconButton:focus-visible {
|
|
51
|
-
outline: 2px solid var(--input-icon-outline, #0066cc);
|
|
52
|
-
outline-offset: 1px;
|
|
53
|
-
}
|
|
54
|
-
</style>
|