lkd-web-kit 0.10.5 → 0.10.7
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 +2 -2
- package/scripts/install-agent-skills.mjs +2 -2
- package/src/distributed-skills/create-modal-component/SKILL.md +168 -0
- package/src/distributed-skills/create-modal-component/agents/openai.yaml +4 -0
- package/src/distributed-skills/create-nextjs-page/SKILL.md +104 -0
- package/src/distributed-skills/create-nextjs-page/agents/openai.yaml +3 -0
- package/src/distributed-skills/create-svg-icon/SKILL.md +41 -0
- package/src/distributed-skills/create-svg-icon/agents/openai.yaml +3 -0
- package/src/distributed-skills/create-table-component/SKILL.md +189 -0
- package/src/distributed-skills/create-table-component/agents/openai.yaml +3 -0
- package/src/distributed-skills/rhf-lkd-forms/SKILL.md +186 -0
- package/.agents/skills/mantine-combobox/SKILL.md +0 -73
- package/.agents/skills/mantine-combobox/references/api.md +0 -199
- package/.agents/skills/mantine-combobox/references/patterns.md +0 -279
- package/.agents/skills/mantine-custom-components/SKILL.md +0 -112
- package/.agents/skills/mantine-custom-components/references/api.md +0 -407
- package/.agents/skills/mantine-custom-components/references/patterns.md +0 -431
- package/.agents/skills/publish-lkd-web-kit/SKILL.md +0 -165
- package/.agents/skills/publish-lkd-web-kit/agents/openai.yaml +0 -4
- package/.agents/skills/publish-lkd-web-kit/references/npm-trusted-publishing.md +0 -82
- package/.agents/skills/update-lkd-dependencies/SKILL.md +0 -88
- package/.agents/skills/update-lkd-dependencies/agents/openai.yaml +0 -4
- package/.agents/skills/update-lkd-dependencies/scripts/collect-npm-metadata.mjs +0 -73
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
# Combobox Implementation Patterns
|
|
2
|
-
|
|
3
|
-
## Table of Contents
|
|
4
|
-
- [Basic select (button trigger)](#basic-select-button-trigger)
|
|
5
|
-
- [Searchable select (input trigger)](#searchable-select-input-trigger)
|
|
6
|
-
- [Multi-select with pills](#multi-select-with-pills)
|
|
7
|
-
- [Options with groups](#options-with-groups)
|
|
8
|
-
- [Custom option rendering](#custom-option-rendering)
|
|
9
|
-
- [Clear button](#clear-button)
|
|
10
|
-
- [Form integration (hidden input)](#form-integration-hidden-input)
|
|
11
|
-
- [Nothing found message](#nothing-found-message)
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## Basic select (button trigger)
|
|
16
|
-
|
|
17
|
-
```tsx
|
|
18
|
-
function CustomSelect({ data }: { data: string[] }) {
|
|
19
|
-
const [value, setValue] = useState<string | null>(null);
|
|
20
|
-
|
|
21
|
-
const combobox = useCombobox({
|
|
22
|
-
onDropdownClose: () => combobox.resetSelectedOption(),
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
const options = data.map((item) => (
|
|
26
|
-
<Combobox.Option value={item} key={item} active={item === value}>
|
|
27
|
-
{item}
|
|
28
|
-
</Combobox.Option>
|
|
29
|
-
));
|
|
30
|
-
|
|
31
|
-
return (
|
|
32
|
-
<Combobox
|
|
33
|
-
store={combobox}
|
|
34
|
-
onOptionSubmit={(val) => {
|
|
35
|
-
setValue(val);
|
|
36
|
-
combobox.closeDropdown();
|
|
37
|
-
}}
|
|
38
|
-
>
|
|
39
|
-
<Combobox.Target targetType="button">
|
|
40
|
-
<InputBase
|
|
41
|
-
component="button"
|
|
42
|
-
type="button"
|
|
43
|
-
pointer
|
|
44
|
-
rightSection={<Combobox.Chevron />}
|
|
45
|
-
rightSectionPointerEvents="none"
|
|
46
|
-
onClick={() => combobox.toggleDropdown()}
|
|
47
|
-
>
|
|
48
|
-
{value || <Input.Placeholder>Pick value</Input.Placeholder>}
|
|
49
|
-
</InputBase>
|
|
50
|
-
</Combobox.Target>
|
|
51
|
-
|
|
52
|
-
<Combobox.Dropdown>
|
|
53
|
-
<Combobox.Options>{options}</Combobox.Options>
|
|
54
|
-
</Combobox.Dropdown>
|
|
55
|
-
</Combobox>
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
---
|
|
61
|
-
|
|
62
|
-
## Searchable select (input trigger)
|
|
63
|
-
|
|
64
|
-
Input acts as both trigger and search field. Filter options based on the typed value.
|
|
65
|
-
|
|
66
|
-
```tsx
|
|
67
|
-
function SearchableSelect({ data }: { data: string[] }) {
|
|
68
|
-
const [value, setValue] = useState<string | null>(null);
|
|
69
|
-
const [search, setSearch] = useState('');
|
|
70
|
-
|
|
71
|
-
const combobox = useCombobox({
|
|
72
|
-
onDropdownClose: () => combobox.resetSelectedOption(),
|
|
73
|
-
onDropdownOpen: () => combobox.selectFirstOption(),
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
const shouldFilterOptions = data.every((item) => item !== search);
|
|
77
|
-
const filtered = shouldFilterOptions
|
|
78
|
-
? data.filter((item) => item.toLowerCase().includes(search.toLowerCase().trim()))
|
|
79
|
-
: data;
|
|
80
|
-
|
|
81
|
-
const options = filtered.length > 0
|
|
82
|
-
? filtered.map((item) => (
|
|
83
|
-
<Combobox.Option value={item} key={item}>{item}</Combobox.Option>
|
|
84
|
-
))
|
|
85
|
-
: <Combobox.Empty>Nothing found</Combobox.Empty>;
|
|
86
|
-
|
|
87
|
-
return (
|
|
88
|
-
<Combobox
|
|
89
|
-
store={combobox}
|
|
90
|
-
onOptionSubmit={(val) => {
|
|
91
|
-
setValue(val);
|
|
92
|
-
setSearch(val);
|
|
93
|
-
combobox.closeDropdown();
|
|
94
|
-
}}
|
|
95
|
-
>
|
|
96
|
-
<Combobox.Target>
|
|
97
|
-
<InputBase
|
|
98
|
-
rightSection={<Combobox.Chevron />}
|
|
99
|
-
value={search}
|
|
100
|
-
onChange={(e) => {
|
|
101
|
-
combobox.openDropdown();
|
|
102
|
-
combobox.updateSelectedOptionIndex();
|
|
103
|
-
setSearch(e.currentTarget.value);
|
|
104
|
-
}}
|
|
105
|
-
onClick={() => combobox.openDropdown()}
|
|
106
|
-
onFocus={() => combobox.openDropdown()}
|
|
107
|
-
onBlur={() => {
|
|
108
|
-
combobox.closeDropdown();
|
|
109
|
-
setSearch(value || '');
|
|
110
|
-
}}
|
|
111
|
-
placeholder="Search value"
|
|
112
|
-
rightSectionPointerEvents="none"
|
|
113
|
-
/>
|
|
114
|
-
</Combobox.Target>
|
|
115
|
-
|
|
116
|
-
<Combobox.Dropdown>
|
|
117
|
-
<Combobox.Options>{options}</Combobox.Options>
|
|
118
|
-
</Combobox.Dropdown>
|
|
119
|
-
</Combobox>
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
---
|
|
125
|
-
|
|
126
|
-
## Multi-select with pills
|
|
127
|
-
|
|
128
|
-
Use `Combobox.DropdownTarget` for the outer container and `Combobox.EventsTarget` around the text input inside it.
|
|
129
|
-
|
|
130
|
-
```tsx
|
|
131
|
-
function MultiSelect({ data }: { data: string[] }) {
|
|
132
|
-
const combobox = useCombobox({ onDropdownClose: () => combobox.resetSelectedOption() });
|
|
133
|
-
const [search, setSearch] = useState('');
|
|
134
|
-
const [value, setValue] = useState<string[]>([]);
|
|
135
|
-
|
|
136
|
-
const handleValueSelect = (val: string) =>
|
|
137
|
-
setValue((current) =>
|
|
138
|
-
current.includes(val) ? current.filter((v) => v !== val) : [...current, val]
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
const handleValueRemove = (val: string) =>
|
|
142
|
-
setValue((current) => current.filter((v) => v !== val));
|
|
143
|
-
|
|
144
|
-
const options = data
|
|
145
|
-
.filter((item) => item.toLowerCase().includes(search.trim().toLowerCase()))
|
|
146
|
-
.map((item) => (
|
|
147
|
-
<Combobox.Option value={item} key={item} active={value.includes(item)}>
|
|
148
|
-
<Group gap="sm">
|
|
149
|
-
{value.includes(item) ? <CheckIcon size={12} /> : null}
|
|
150
|
-
<span>{item}</span>
|
|
151
|
-
</Group>
|
|
152
|
-
</Combobox.Option>
|
|
153
|
-
));
|
|
154
|
-
|
|
155
|
-
const pills = value.map((item) => (
|
|
156
|
-
<Pill key={item} withRemoveButton onRemove={() => handleValueRemove(item)}>
|
|
157
|
-
{item}
|
|
158
|
-
</Pill>
|
|
159
|
-
));
|
|
160
|
-
|
|
161
|
-
return (
|
|
162
|
-
<Combobox store={combobox} onOptionSubmit={handleValueSelect}>
|
|
163
|
-
<Combobox.DropdownTarget>
|
|
164
|
-
<PillsInput onClick={() => combobox.openDropdown()}>
|
|
165
|
-
<Pill.Group>
|
|
166
|
-
{pills}
|
|
167
|
-
<Combobox.EventsTarget>
|
|
168
|
-
<PillsInput.Field
|
|
169
|
-
onFocus={() => combobox.openDropdown()}
|
|
170
|
-
onBlur={() => combobox.closeDropdown()}
|
|
171
|
-
value={search}
|
|
172
|
-
placeholder="Search values"
|
|
173
|
-
onChange={(e) => {
|
|
174
|
-
combobox.updateSelectedOptionIndex();
|
|
175
|
-
setSearch(e.currentTarget.value);
|
|
176
|
-
}}
|
|
177
|
-
onKeyDown={(e) => {
|
|
178
|
-
if (e.key === 'Backspace' && search.length === 0) {
|
|
179
|
-
e.preventDefault();
|
|
180
|
-
handleValueRemove(value[value.length - 1]);
|
|
181
|
-
}
|
|
182
|
-
}}
|
|
183
|
-
/>
|
|
184
|
-
</Combobox.EventsTarget>
|
|
185
|
-
</Pill.Group>
|
|
186
|
-
</PillsInput>
|
|
187
|
-
</Combobox.DropdownTarget>
|
|
188
|
-
|
|
189
|
-
<Combobox.Dropdown>
|
|
190
|
-
<Combobox.Options>
|
|
191
|
-
{options.length > 0 ? options : <Combobox.Empty>Nothing found</Combobox.Empty>}
|
|
192
|
-
</Combobox.Options>
|
|
193
|
-
</Combobox.Dropdown>
|
|
194
|
-
</Combobox>
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
---
|
|
200
|
-
|
|
201
|
-
## Options with groups
|
|
202
|
-
|
|
203
|
-
```tsx
|
|
204
|
-
const groups = data.map((group) => (
|
|
205
|
-
<Combobox.Group label={group.group} key={group.group}>
|
|
206
|
-
{group.items.map((item) => (
|
|
207
|
-
<Combobox.Option value={item.value} key={item.value}>
|
|
208
|
-
{item.label}
|
|
209
|
-
</Combobox.Option>
|
|
210
|
-
))}
|
|
211
|
-
</Combobox.Group>
|
|
212
|
-
));
|
|
213
|
-
|
|
214
|
-
// Inside Combobox.Dropdown:
|
|
215
|
-
<Combobox.Options>{groups}</Combobox.Options>
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
---
|
|
219
|
-
|
|
220
|
-
## Custom option rendering
|
|
221
|
-
|
|
222
|
-
Render any content inside `Combobox.Option`:
|
|
223
|
-
|
|
224
|
-
```tsx
|
|
225
|
-
const options = data.map((item) => (
|
|
226
|
-
<Combobox.Option value={item.value} key={item.value}>
|
|
227
|
-
<Group>
|
|
228
|
-
<Avatar src={item.avatar} size="sm" />
|
|
229
|
-
<div>
|
|
230
|
-
<Text size="sm">{item.label}</Text>
|
|
231
|
-
<Text size="xs" c="dimmed">{item.email}</Text>
|
|
232
|
-
</div>
|
|
233
|
-
</Group>
|
|
234
|
-
</Combobox.Option>
|
|
235
|
-
));
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
---
|
|
239
|
-
|
|
240
|
-
## Clear button
|
|
241
|
-
|
|
242
|
-
```tsx
|
|
243
|
-
const rightSection = value ? (
|
|
244
|
-
<Combobox.ClearButton onClear={() => { setValue(null); setSearch(''); }} />
|
|
245
|
-
) : (
|
|
246
|
-
<Combobox.Chevron />
|
|
247
|
-
);
|
|
248
|
-
|
|
249
|
-
<InputBase
|
|
250
|
-
rightSection={rightSection}
|
|
251
|
-
rightSectionPointerEvents={value ? 'all' : 'none'}
|
|
252
|
-
/>
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
---
|
|
256
|
-
|
|
257
|
-
## Form integration (hidden input)
|
|
258
|
-
|
|
259
|
-
```tsx
|
|
260
|
-
// Single value
|
|
261
|
-
<Combobox.HiddenInput value={value} name="framework" />
|
|
262
|
-
|
|
263
|
-
// Multiple values (joined by comma by default)
|
|
264
|
-
<Combobox.HiddenInput value={selectedValues} name="frameworks" valuesDivider="," />
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
---
|
|
268
|
-
|
|
269
|
-
## Nothing found message
|
|
270
|
-
|
|
271
|
-
```tsx
|
|
272
|
-
<Combobox.Dropdown>
|
|
273
|
-
<Combobox.Options>
|
|
274
|
-
{filteredOptions.length > 0
|
|
275
|
-
? filteredOptions
|
|
276
|
-
: <Combobox.Empty>Nothing found for "{search}"</Combobox.Empty>}
|
|
277
|
-
</Combobox.Options>
|
|
278
|
-
</Combobox.Dropdown>
|
|
279
|
-
```
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: mantine-custom-components
|
|
3
|
-
description: >
|
|
4
|
-
Build custom components that integrate with Mantine's theming, Styles API, and core features.
|
|
5
|
-
Use this skill when: (1) creating a new component using factory(), polymorphicFactory(), or
|
|
6
|
-
genericFactory(), (2) adding Styles API support (classNames, styles, vars, unstyled), (3)
|
|
7
|
-
implementing CSS variables via createVarsResolver, (4) building compound components with
|
|
8
|
-
sub-components and shared context, (5) registering a component with MantineProvider via
|
|
9
|
-
Component.extend(), or (6) any task involving Factory, useProps, useStyles, BoxProps,
|
|
10
|
-
StylesApiProps, or ElementProps in @mantine/core.
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
# Mantine Custom Components Skill
|
|
14
|
-
|
|
15
|
-
## Component template
|
|
16
|
-
|
|
17
|
-
```tsx
|
|
18
|
-
import {
|
|
19
|
-
Box, BoxProps, createVarsResolver, ElementProps,
|
|
20
|
-
factory, Factory, getRadius, MantineRadius,
|
|
21
|
-
StylesApiProps, useProps, useStyles,
|
|
22
|
-
} from '@mantine/core';
|
|
23
|
-
import classes from './MyComponent.module.css';
|
|
24
|
-
|
|
25
|
-
export type MyComponentStylesNames = 'root' | 'inner';
|
|
26
|
-
export type MyComponentVariant = 'filled' | 'outline';
|
|
27
|
-
export type MyComponentCssVariables = { root: '--my-radius' };
|
|
28
|
-
|
|
29
|
-
export interface MyComponentProps
|
|
30
|
-
extends BoxProps, StylesApiProps<MyComponentFactory>, ElementProps<'div'> {
|
|
31
|
-
radius?: MantineRadius;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export type MyComponentFactory = Factory<{
|
|
35
|
-
props: MyComponentProps;
|
|
36
|
-
ref: HTMLDivElement;
|
|
37
|
-
stylesNames: MyComponentStylesNames;
|
|
38
|
-
vars: MyComponentCssVariables;
|
|
39
|
-
variant: MyComponentVariant;
|
|
40
|
-
}>;
|
|
41
|
-
|
|
42
|
-
const defaultProps = { radius: 'md' } satisfies Partial<MyComponentProps>;
|
|
43
|
-
|
|
44
|
-
const varsResolver = createVarsResolver<MyComponentFactory>((_theme, { radius }) => ({
|
|
45
|
-
root: { '--my-radius': getRadius(radius) },
|
|
46
|
-
}));
|
|
47
|
-
|
|
48
|
-
export const MyComponent = factory<MyComponentFactory>((_props) => {
|
|
49
|
-
const props = useProps('MyComponent', defaultProps, _props);
|
|
50
|
-
const { classNames, className, style, styles, unstyled, vars, attributes, radius, ...others } = props;
|
|
51
|
-
|
|
52
|
-
const getStyles = useStyles<MyComponentFactory>({
|
|
53
|
-
name: 'MyComponent', classes, props,
|
|
54
|
-
className, style, classNames, styles, unstyled, vars, attributes, varsResolver,
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
return <Box {...getStyles('root')} {...others} />;
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
MyComponent.displayName = '@mantine/core/MyComponent';
|
|
61
|
-
MyComponent.classes = classes;
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
## Factory variant — which to use
|
|
65
|
-
|
|
66
|
-
| Scenario | Factory function | Type |
|
|
67
|
-
|---|---|---|
|
|
68
|
-
| Standard component | `factory()` | `Factory<{}>` |
|
|
69
|
-
| Supports `component` prop (polymorphic) | `polymorphicFactory()` | `PolymorphicFactory<{}>` — add `defaultComponent` and `defaultRef` |
|
|
70
|
-
| Props change based on a generic (e.g. `multiple`) | `genericFactory()` | `Factory<{ signature: ... }>` |
|
|
71
|
-
|
|
72
|
-
Use `polymorphicFactory` sparingly — it adds TypeScript overhead and slows IDE autocomplete.
|
|
73
|
-
|
|
74
|
-
## Factory type fields
|
|
75
|
-
|
|
76
|
-
```ts
|
|
77
|
-
Factory<{
|
|
78
|
-
props: MyComponentProps; // required
|
|
79
|
-
ref: HTMLDivElement; // element type for the forwarded ref
|
|
80
|
-
stylesNames: 'root' | 'inner'; // union of Styles API selectors
|
|
81
|
-
vars: { root: '--my-var' }; // CSS variable map per selector
|
|
82
|
-
variant: 'filled' | 'outline'; // accepted variant strings
|
|
83
|
-
staticComponents: { // sub-components (compound pattern)
|
|
84
|
-
Item: typeof MyComponentItem;
|
|
85
|
-
};
|
|
86
|
-
compound?: boolean; // true = sub-component; disables theme classNames/styles/vars
|
|
87
|
-
ctx?: MyContextType; // passed to styles/vars resolvers as third arg
|
|
88
|
-
signature?: (...) => JSX.Element; // only for genericFactory
|
|
89
|
-
}>
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
## Theme integration
|
|
93
|
-
|
|
94
|
-
Users and the theme can override defaults via `Component.extend()`:
|
|
95
|
-
|
|
96
|
-
```ts
|
|
97
|
-
const theme = createTheme({
|
|
98
|
-
components: {
|
|
99
|
-
MyComponent: MyComponent.extend({
|
|
100
|
-
defaultProps: { radius: 'xl' },
|
|
101
|
-
classNames: { root: 'my-root' },
|
|
102
|
-
styles: { root: { color: 'red' } },
|
|
103
|
-
vars: (_theme, props) => ({ root: { '--my-radius': getRadius(props.radius) } }),
|
|
104
|
-
}),
|
|
105
|
-
},
|
|
106
|
-
});
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
## References
|
|
110
|
-
|
|
111
|
-
- **[`references/api.md`](references/api.md)** — All imports: `factory`, `useProps`, `useStyles`, `createVarsResolver`, `createSafeContext`, `StylesApiProps`, `CompoundStylesApiProps`, `BoxProps`, `ElementProps`, theme helpers (`getSize`, `getRadius`, etc.)
|
|
112
|
-
- **[`references/patterns.md`](references/patterns.md)** — Full examples: compound components with context, polymorphic component, generic component, theme integration
|