Skip to content

Commit 82f72e5

Browse files
committed
feat: Initial implementation of select
1 parent 85747a0 commit 82f72e5

File tree

13 files changed

+529
-6
lines changed

13 files changed

+529
-6
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import { Component, For } from 'solid-js';
2+
import { Container, Heading, Divider, Select } from '@spuxx/solid';
3+
import { SelectOption } from '@spuxx/browser-utils';
4+
5+
export const SelectRoute: Component = () => {
6+
const variants = ['contained', 'outlined'];
7+
const options: SelectOption[] = [
8+
{ value: 'apple', label: 'Apple' },
9+
{ value: 'banana', label: 'Banana' },
10+
{ value: 'cherry', label: 'Cherry' },
11+
];
12+
13+
const handleChange = (value: unknown) => {
14+
console.log(value);
15+
};
16+
17+
return (
18+
<>
19+
<Container tag="article">
20+
<Heading level={1}>Input</Heading>
21+
<Divider color="gradient" />
22+
<Container variant="contained" color="background">
23+
<Heading level={2}>Variants</Heading>
24+
<Divider color="gradient" />
25+
<Select
26+
label="contained (default)"
27+
options={options}
28+
size="small"
29+
class="m-1"
30+
onChange={handleChange}
31+
/>
32+
<Select
33+
label="outlined"
34+
options={options}
35+
variant="outlined"
36+
size="small"
37+
class="m-1"
38+
onChange={handleChange}
39+
/>
40+
</Container>
41+
<Container variant="contained" color="background">
42+
<Heading level={2}>Initial Value</Heading>
43+
<Divider color="gradient" />
44+
<Select
45+
label="With option"
46+
default={options[1]}
47+
options={options}
48+
class="m-1"
49+
size="small"
50+
onChange={handleChange}
51+
/>
52+
<Select
53+
label="With index"
54+
default={1}
55+
options={options}
56+
class="m-1"
57+
size="small"
58+
onChange={handleChange}
59+
/>
60+
</Container>
61+
<Container variant="contained" color="background">
62+
<Heading level={2}>Sizes</Heading>
63+
<Divider color="gradient" />
64+
<For each={variants}>
65+
{(variant) => (
66+
<>
67+
<Select
68+
label="auto (default)"
69+
options={options}
70+
class="m-1"
71+
variant={variant}
72+
onChange={handleChange}
73+
/>
74+
<Select
75+
label="small"
76+
options={options}
77+
class="m-1"
78+
size="small"
79+
variant={variant}
80+
onChange={handleChange}
81+
/>
82+
<Select
83+
label="medium"
84+
options={options}
85+
class="m-1"
86+
size="medium"
87+
variant={variant}
88+
onChange={handleChange}
89+
/>
90+
<Select
91+
label="large"
92+
options={options}
93+
class="m-1"
94+
size="large"
95+
variant={variant}
96+
onChange={handleChange}
97+
/>
98+
<Select
99+
label="full"
100+
options={options}
101+
class="m-1"
102+
size="full"
103+
variant={variant}
104+
onChange={handleChange}
105+
/>
106+
</>
107+
)}
108+
</For>
109+
</Container>
110+
<Container variant="contained" color="background">
111+
<Heading level={2}>With Icon</Heading>
112+
<Divider color="gradient" />
113+
<For each={variants}>
114+
{(variant) => (
115+
<>
116+
<Select
117+
label="Fruit"
118+
class="m-1"
119+
icon="mdi:fruit-cherries"
120+
size="small"
121+
variant={variant}
122+
options={options}
123+
onChange={handleChange}
124+
/>
125+
<Select
126+
label="Fruit"
127+
class="m-1"
128+
icon="mdi:fruit-cherries"
129+
size="small"
130+
variant={variant}
131+
options={options}
132+
onChange={handleChange}
133+
/>
134+
</>
135+
)}
136+
</For>
137+
</Container>
138+
</Container>
139+
</>
140+
);
141+
};

apps/solid/src/routes/routes.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { ContainerRoute } from './components/layout/container.route';
55
import { DividerRoute } from './components/layout/divider.route';
66
import { DialogRoute } from './dialog.route';
77
import { InputRoute } from './components/control/input.route';
8+
import { SelectRoute } from './components/control/select.route';
89

910
export const routes: RouteProps[] = [
1011
{
@@ -19,6 +20,10 @@ export const routes: RouteProps[] = [
1920
path: '/components/control/input',
2021
component: () => InputRoute,
2122
},
23+
{
24+
path: '/components/control/select',
25+
component: () => SelectRoute,
26+
},
2227
{
2328
path: '/components/layout/container',
2429
component: () => ContainerRoute,
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
@import './components/control/button.css';
22
@import './components/control/input.css';
3+
@import './components/control/select.css';
34
@import './components/layout/divider.css';
45
@import './components/layout/container.css';
56
@import './components/typography/heading.css';

packages/browser-utils/src/styles/components/control/input.css

-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
.spx-input {
2-
--spx-input-pd-x: 0.75rem;
3-
42
position: relative;
53
height: fit-content;
64
display: inline-flex;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
.spx-select {
2+
position: relative;
3+
height: fit-content;
4+
display: inline-flex;
5+
border-radius: var(--spx-border-radius);
6+
background: inherit;
7+
pointer-events: none;
8+
9+
&:has(select:disabled) {
10+
filter: var(--spx-filter-control-disabled);
11+
}
12+
13+
&[spx-size='small'] {
14+
width: var(--spx-control-width-small);
15+
max-width: 100%;
16+
}
17+
18+
&[spx-size='medium'] {
19+
width: var(--spx-control-width-medium);
20+
max-width: 100%;
21+
}
22+
23+
&[spx-size='large'] {
24+
width: var(--spx-control-width-large);
25+
max-width: 100%;
26+
}
27+
28+
select {
29+
height: var(--spx-input-height-default);
30+
line-height: var(--spx-input-height-default);
31+
width: 100%;
32+
box-sizing: border-box;
33+
font-size: medium;
34+
background: inherit;
35+
border: none;
36+
border-radius: var(--spx-border-radius);
37+
padding: 0px var(--spx-input-pd-x);
38+
pointer-events: all;
39+
-webkit-appearance: none;
40+
-moz-appearance: none;
41+
}
42+
43+
select:focus-visible {
44+
outline: none;
45+
}
46+
47+
label {
48+
position: absolute;
49+
font-size: medium;
50+
top: 50%;
51+
left: var(--spx-input-pd-x);
52+
pointer-events: none;
53+
display: flex;
54+
gap: 2px;
55+
align-items: center;
56+
font-size: smaller;
57+
width: max-content;
58+
}
59+
60+
> iconify-icon {
61+
position: absolute;
62+
top: 50%;
63+
right: calc(var(--spx-input-pd-x) / 2);
64+
transform: translateY(-50%);
65+
pointer-events: none;
66+
font-size: x-large;
67+
color: var(--spx-color-text-subtle);
68+
}
69+
}
70+
71+
.spx-select[spx-variant='contained'] {
72+
border-bottom-left-radius: 0px;
73+
border-bottom-right-radius: 0px;
74+
background: var(--spx-color-input-contained-bg);
75+
76+
select {
77+
color: inherit;
78+
box-sizing: border-box;
79+
border-bottom-left-radius: 0px;
80+
border-bottom-right-radius: 0px;
81+
padding-top: 0.65rem;
82+
}
83+
84+
label {
85+
transform: translateY(-1.15rem);
86+
color: var(--spx-color-text-subtle);
87+
}
88+
89+
&:has(select:focus) {
90+
select:focus {
91+
box-shadow: 0 var(--spx-border-width) 0 var(--spx-color-input-focus);
92+
}
93+
94+
label {
95+
color: var(--spx-color-input-focus);
96+
}
97+
}
98+
}
99+
100+
.spx-select[spx-variant='outlined'] {
101+
background: inherit;
102+
color: var(--spx-color-input-outlined-fg);
103+
border-color: var(--spx-color-input-outlined-fg);
104+
105+
select {
106+
color: var(--spx-color-text-default);
107+
}
108+
109+
label {
110+
transform: translateY(-2.15rem);
111+
padding: 0px 4px;
112+
left: calc(var(--spx-input-pd-x) - 4px);
113+
background: inherit;
114+
}
115+
116+
&:has(select:focus) {
117+
border-color: var(--spx-color-input-focus);
118+
119+
label {
120+
color: var(--spx-color-input-focus);
121+
}
122+
}
123+
}

packages/browser-utils/src/styles/variables.css

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
--spx-control-height-default: 40px;
55
--spx-input-height-default: 48px;
66

7-
--spx-control-width-small: 8rem;
7+
--spx-control-width-small: 10rem;
88
--spx-control-width-medium: 16rem;
99
--spx-control-width-large: 24rem;
1010

11+
--spx-input-pd-x: 0.75rem;
1112
--spx-border-width: 1px;
1213
}

packages/solid/src/components/control/input/input.component.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ export const Input: Component<InputProps> = (props) => {
2424
options && forceOption ? options.map((o) => o.value).join('|') : props.attrs?.pattern;
2525

2626
const handleChange: JSX.EventHandler<HTMLInputElement, Event> = (event) => {
27-
const input = event.currentTarget as HTMLInputElement;
27+
const input = event.currentTarget;
2828
if (props.onChange) props.onChange(input.value, event);
2929
};
3030

3131
const handleInput: JSX.EventHandler<HTMLInputElement, Event> = (event) => {
32-
const input = event.currentTarget as HTMLInputElement;
32+
const input = event.currentTarget;
3333
if (props.onInput) props.onInput(input.value, event);
3434
};
3535

packages/solid/src/components/control/input/input.types.ts

-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ export interface InputProps extends ComponentProps<JSX.InputHTMLAttributes<HTMLI
3737
/**
3838
* The type of the input.
3939
*/
40-
// type?: JSX.HTMLAttributes<HTMLInputElement>['min'];
4140
/**
4241
* The input variant.
4342
* @default 'contained'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './select.component';
2+
export * from './select.types';

0 commit comments

Comments
 (0)