Skip to content

Commit 6d45bc1

Browse files
authored
feat(controls): Add new Select control component
1 parent e359afa commit 6d45bc1

File tree

23 files changed

+1051
-179
lines changed

23 files changed

+1051
-179
lines changed

apps/solid/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"license": "MIT",
1313
"devDependencies": {
1414
"@iconify-icon/solid": "2.3.0",
15+
"@tailwindcss/vite": "^4.0.14",
1516
"autoprefixer": "10.4.20",
1617
"postcss": "8.5.1",
1718
"tailwindcss": "4.0.12",

apps/solid/postcss.config.js

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

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,

apps/solid/src/styles/index.css

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
/* @tailwind base; */
2-
@tailwind components;
3-
@tailwind utilities;
1+
@import 'tailwindcss';

apps/solid/vite.config.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { defineConfig } from 'vite';
22
import solidPlugin from 'vite-plugin-solid';
3+
import tailwindcss from '@tailwindcss/vite';
34

45
export default defineConfig({
5-
plugins: [solidPlugin()],
6+
plugins: [solidPlugin(), tailwindcss()],
67
build: {
78
target: 'esnext',
89
},

apps/starlight/src/components/interactive/InteractiveControls.tsx

+26-21
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Container } from '@packages/solid';
2-
import { createSignal, For, type Component } from 'solid-js';
2+
import { createSignal, For, Show, type Component } from 'solid-js';
33
import { isEmptyOrWhitespace } from '@spuxx/js-utils';
44
import type { Argument } from './types';
55

@@ -10,6 +10,7 @@ interface Props {
1010

1111
export const InteractiveControls: Component<Props> = (props) => {
1212
const { argDefinitions, onArgsChange } = props;
13+
1314
const [args, setArgs] = createSignal<Record<string, unknown>>({});
1415

1516
const handleArgsChange = (event: Event) => {
@@ -23,26 +24,30 @@ export const InteractiveControls: Component<Props> = (props) => {
2324
};
2425

2526
const controls = Object.entries(argDefinitions).map(([key, def]) => (
26-
<label>
27-
{key}
28-
{': '}
29-
{def.type === 'boolean' && (
30-
<input
31-
type="checkbox"
32-
name={key}
33-
checked={args()[key] as boolean}
34-
on:change={handleArgsChange}
35-
/>
36-
)}
37-
{def.options && (
38-
<select name={key} on:change={handleArgsChange}>
39-
{!def.default && <option label="Use default value"></option>}
40-
<For each={def.options}>
41-
{(option) => <option selected={option === def.default}>{String(option)}</option>}
42-
</For>
43-
</select>
44-
)}
45-
</label>
27+
<Show when={!def.hide}>
28+
<label>
29+
{key}
30+
{': '}
31+
{def.type === 'boolean' && (
32+
<input
33+
type="checkbox"
34+
name={key}
35+
checked={args()[key] as boolean}
36+
on:change={handleArgsChange}
37+
/>
38+
)}
39+
{def.options && (
40+
<select name={key} on:change={handleArgsChange}>
41+
{!def.default && <option label="Use default value"></option>}
42+
<For each={def.options}>
43+
{(option) => (
44+
<option selected={option === def.default || undefined}>{String(option)}</option>
45+
)}
46+
</For>
47+
</select>
48+
)}
49+
</label>
50+
</Show>
4651
));
4752

4853
return (

apps/starlight/src/components/interactive/InteractiveSolid.tsx

+7
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,18 @@ export const InteractiveSolid: Component<Props> = (props) => {
3030
importComponent();
3131

3232
const handleArgsChange = (args: Record<string, unknown>) => {
33+
for (const [key, def] of Object.entries(argDefinitions)) {
34+
if (!args[key]) args[key] = def.default;
35+
}
3336
setState({ component: state().component!, args });
3437
};
3538

3639
createEffect(() => {
3740
const { component, args } = state();
41+
if (Object.keys(args).length === 0) {
42+
handleArgsChange(args);
43+
return;
44+
}
3845
if (dispose) {
3946
dispose();
4047
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export interface Argument<T> {
22
type: 'string' | 'number' | 'boolean';
3+
hide?: boolean;
34
default?: T;
45
options?: T[];
56
}

apps/starlight/src/content/docs/solid/components/control/input.mdx

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { Interactive } from '@src/components/interactive';
1313
label: {
1414
type: 'string',
1515
options: ['Username', 'Email', 'Password'],
16+
default: 'Username',
1617
},
1718
type: {
1819
type: 'string',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
---
2+
title: 'Select'
3+
description: 'A flexible select component.'
4+
---
5+
6+
import TypeDoc from '@docs/solid/components/control/select.md';
7+
import { BaseColor, ControlSize, InputType } from '@packages/browser-utils';
8+
import { Interactive } from '@src/components/interactive';
9+
10+
<Interactive
11+
componentName="Select"
12+
argDefinitions={{
13+
label: {
14+
type: 'string',
15+
options: ['Fruits', 'Cars', 'Animals'],
16+
default: 'Fruits',
17+
},
18+
options: {
19+
type: 'string',
20+
hide: true,
21+
default: [
22+
{ label: 'Option 1', value: 'option-1' },
23+
{ label: 'Option 2', value: 'option-2' },
24+
{ label: 'Option 3', value: 'option-3' },
25+
],
26+
},
27+
variant: {
28+
type: 'string',
29+
options: ['contained', 'outlined'],
30+
default: 'contained',
31+
},
32+
icon: {
33+
type: 'string',
34+
options: ['mdi:star', 'mdi:check', 'mdi:login'],
35+
},
36+
size: {
37+
type: 'string',
38+
options: Object.values(ControlSize),
39+
default: 'small',
40+
},
41+
disabled: {
42+
type: 'boolean',
43+
},
44+
}}
45+
>
46+
Click me!
47+
</Interactive>
48+
49+
<TypeDoc />

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

+1
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/app-bar.css';
45
@import './components/layout/divider.css';
56
@import './components/layout/container.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;

0 commit comments

Comments
 (0)