@stamcat/craftsman 0.0.31-beta.3 → 0.0.31
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/AGENTS.md +5 -4
- package/Components.esm.js +78 -76
- package/package.json +7 -1
- package/skills/craftsman-component-usage/SKILL.md +156 -0
- package/skills/craftsman-device-detection/SKILL.md +79 -0
- package/skills/craftsman-style-utilities/SKILL.md +228 -0
- package/skills/craftsman-utility-functions/SKILL.md +71 -0
- package/src/components/Accordion/AGENTS.md +45 -0
- package/src/components/Accordion/Accordion.css +1 -0
- package/src/components/Accordion/Accordion.d.ts +14 -0
- package/src/components/Accordion/Accordion.scss +64 -0
- package/src/components/Accordion/Accordion2.esm.js +70 -0
- package/src/components/Carousel/Carousel2.esm.js +21 -21
- package/src/components/DatePicker/DatePicker.css +1 -1
- package/src/components/DatePicker/DatePicker2.esm.js +11 -11
- package/src/components/DateRangePicker/DateRangePicker.css +1 -1
- package/src/components/DateRangePicker/DateRangePicker2.esm.js +10 -10
- package/src/components/DateTimePicker/DateTimePicker.css +1 -1
- package/src/components/DateTimePicker/DateTimePicker2.esm.js +14 -14
- package/src/components/Input/Input.css +1 -1
- package/src/components/Input/Input.scss +7 -0
- package/src/components/Input/Input2.esm.js +10 -10
- package/src/components/InputNumber/InputNumber2.esm.js +10 -10
- package/src/components/InputPassword/InputPassword.esm.js +9 -9
- package/src/components/InputPhone/InputPhone2.esm.js +7 -7
- package/src/components/Modal/Modal2.esm.js +15 -15
- package/src/components/Pagination/Pagination2.esm.js +1 -1
- package/src/components/Select/Select.esm.js +18 -17
- package/src/components/SortableList/ListItem.esm.js +8 -8
- package/src/components/SortableList/SortableList2.esm.js +8 -8
- package/src/components/Text/AGENTS.md +4 -2
- package/src/components/Textarea/Textarea.esm.js +5 -5
- package/src/components/TimePicker/TimePicker.css +1 -1
- package/src/components/TimePicker/TimePicker2.esm.js +15 -15
- package/src/components/TimePicker/TimePickerDisplay.esm.js +1 -1
- package/src/components/TimePicker/TimePickerWheel.esm.js +15 -15
- package/src/components/Tooltip/Tooltip2.esm.js +12 -12
- package/src/components/index.d.ts +1 -0
- package/src/styles/global/components/_input.scss +8 -9
- package/src/styles/global/components/_select.scss +11 -16
- package/src/utilities/types.d.ts +14 -14
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: craftsman-style-utilities
|
|
3
|
+
description: 'Styling conventions for @stamcat/craftsman — color()/width()/breakpoint() TypeScript and Sass helpers, styling expectations, and theme authoring (theme.root, theme.components, theme.widths). Use when writing styles, CSS-in-JS, Sass, or ThemeProvider theme objects for @stamcat/craftsman.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Craftsman Style Utilities
|
|
7
|
+
|
|
8
|
+
Styling rules for AI agents generating code against `@stamcat/craftsman`. For package exports, hard rules, and component contracts, see the [craftsman-component-usage skill](../craftsman-component-usage/SKILL.md). For non-styling utility functions (`isEmpty`), see the [craftsman-utility-functions skill](../craftsman-utility-functions/SKILL.md).
|
|
9
|
+
|
|
10
|
+
## General Sass Guidance
|
|
11
|
+
|
|
12
|
+
When writing styles for more than 2 elements in an app that uses Sass, prefer a placeholder (`%placeholder` + `@extend`) over string concatenation to share the rule set.
|
|
13
|
+
|
|
14
|
+
Prefer one meaningful class name per component root and rely on HTML5 element semantics plus Sass nesting to style descendants — not every element needs its own `className`.
|
|
15
|
+
|
|
16
|
+
```scss
|
|
17
|
+
// Preferred: one root class, nested element selectors
|
|
18
|
+
.card {
|
|
19
|
+
padding: #{u.width(gutter)};
|
|
20
|
+
|
|
21
|
+
h3 {
|
|
22
|
+
margin-bottom: #{u.width(gutter, 0.5)};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
p {
|
|
26
|
+
color: #{u.color(gray500)};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
button {
|
|
30
|
+
margin-top: #{u.width(gutter)};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```scss
|
|
36
|
+
// Avoid: a className on every descendant when nesting on semantic tags is sufficient
|
|
37
|
+
.card { }
|
|
38
|
+
.card-title { }
|
|
39
|
+
.card-body { }
|
|
40
|
+
.card-action { }
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Prefer resolving color and spacing through the `color()`/`width()` functions and mixins when the value is actually derived from a globally declared variable — for example, a width that is exactly half the declared gutter should use `width("gutter", 0.5)`. If a value isn't a derivation of a global variable (e.g. gutter is `16px` but a component needs `14px`), use the literal value directly rather than forcing it through a function.
|
|
44
|
+
|
|
45
|
+
Preferred page/layout pattern:
|
|
46
|
+
|
|
47
|
+
- Use one primary page/layout wrapper class and style semantic descendants via nesting (`> header`, `> main`, `> section`, etc.).
|
|
48
|
+
- Avoid adding class names to semantic sectioning elements only to mirror old style objects; prefer structural selectors first.
|
|
49
|
+
- Introduce small helper classes or placeholders only where they add clear value: repeated layout primitives (for example shared row/stack wrappers) or stateful/dynamic visual states.
|
|
50
|
+
- Keep the DOM semantic and minimal; avoid class-heavy markup when nested selectors can express the same styling.
|
|
51
|
+
|
|
52
|
+
## Sass Import Path (hard rule)
|
|
53
|
+
|
|
54
|
+
Always `@use` the published subpath — never deep-import the source file directly.
|
|
55
|
+
|
|
56
|
+
```scss
|
|
57
|
+
// Correct
|
|
58
|
+
@use "@stamcat/craftsman/styles/utilities/functions" as u;
|
|
59
|
+
@use "@stamcat/craftsman/styles/utilities/mixins" as m;
|
|
60
|
+
@use "@stamcat/craftsman/styles/utilities/placeholders";
|
|
61
|
+
|
|
62
|
+
// Wrong — not an exported subpath, will fail to resolve at build time
|
|
63
|
+
@use "@stamcat/craftsman/src/styles/utilities/functions" as u;
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Style Utilities
|
|
67
|
+
|
|
68
|
+
Craftsman provides TypeScript helpers and matching Sass functions for color, spacing, and breakpoints. Always use these instead of hard-coded values so theming and overrides work correctly.
|
|
69
|
+
|
|
70
|
+
### color()
|
|
71
|
+
|
|
72
|
+
**TypeScript** — import from `@stamcat/craftsman/styles`:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import { color, colors, hexToRgba } from "@stamcat/craftsman/styles";
|
|
76
|
+
|
|
77
|
+
// Returns var(--blue500)
|
|
78
|
+
color("blue500")
|
|
79
|
+
|
|
80
|
+
// Returns rgb(from var(--blue500) r g b / 0.5)
|
|
81
|
+
color("blue500", "rgba", 0.5)
|
|
82
|
+
|
|
83
|
+
// Fallback for environments that don't support CSS relative color syntax
|
|
84
|
+
hexToRgba(colors.blue500, 0.5)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Sass** — import `functions` as `u`:
|
|
88
|
+
|
|
89
|
+
```scss
|
|
90
|
+
@use "@stamcat/craftsman/styles/utilities/functions" as u;
|
|
91
|
+
|
|
92
|
+
.element {
|
|
93
|
+
color: #{u.color(blue500)};
|
|
94
|
+
background: #{u.color(black, rgba, 0.4)};
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
- `color(name)` → `var(--name)` — always prefer this over hard-coded hex values so theme overrides apply.
|
|
99
|
+
- `color(name, rgba, alpha)` → `rgb(from var(--name) r g b / alpha)` — uses CSS relative color syntax; verify browser support for your target.
|
|
100
|
+
- Never hard-code hex color values. Always use `color()` or a CSS variable.
|
|
101
|
+
|
|
102
|
+
### width()
|
|
103
|
+
|
|
104
|
+
**TypeScript** — import from `@stamcat/craftsman/styles`:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import { width } from "@stamcat/craftsman/styles";
|
|
108
|
+
|
|
109
|
+
width("gutter") // var(--w-gutter, 16px)
|
|
110
|
+
width("gutter", 0.5) // calc(var(--w-gutter, 16px) * 0.5)
|
|
111
|
+
width("column", 3) // calc((var(--w-column) * 3) + (var(--w-gutter) * 2))
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Sass** — import `functions` as `u`:
|
|
115
|
+
|
|
116
|
+
```scss
|
|
117
|
+
@use "@stamcat/craftsman/styles/utilities/functions" as u;
|
|
118
|
+
|
|
119
|
+
.card {
|
|
120
|
+
padding: #{u.width(gutter)};
|
|
121
|
+
gap: #{u.width(gutter, 0.5)};
|
|
122
|
+
max-width: #{u.width(column, 4)};
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Valid keys: `text` · `gutter` · `column` · `tablet` · `desktop` · `extDesktop` · `mobileMax` · `tabletMax` · `desktopMax`
|
|
127
|
+
|
|
128
|
+
- The `column` key automatically accounts for gutters between columns.
|
|
129
|
+
- Never use hard-coded `px` values for spacing or layout widths. Always use `width()`.
|
|
130
|
+
|
|
131
|
+
### breakpoint()
|
|
132
|
+
|
|
133
|
+
**TypeScript** — returns a full `@media` rule string for CSS-in-JS:
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
import { breakpoint, media } from "@stamcat/craftsman/styles";
|
|
137
|
+
|
|
138
|
+
// Full rule — use inside styled-components, emotion, or style injection
|
|
139
|
+
breakpoint("desktop", "font-size: 18px;")
|
|
140
|
+
// => "@media (min-width: 1040px) { font-size: 18px; }"
|
|
141
|
+
|
|
142
|
+
// Raw query string only — use for conditional logic or matchMedia
|
|
143
|
+
media.tablet // => "(min-width: 660px)"
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Sass** — `@include breakpoint(key)` wraps content in the correct `@media` query:
|
|
147
|
+
|
|
148
|
+
```scss
|
|
149
|
+
@use "@stamcat/craftsman/styles/utilities/functions" as u;
|
|
150
|
+
|
|
151
|
+
.sidebar {
|
|
152
|
+
display: none;
|
|
153
|
+
|
|
154
|
+
@include u.breakpoint(tablet) {
|
|
155
|
+
display: block;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Available breakpoint keys: `tablet` · `tabletMax` · `tabletOnly` · `desktop` · `desktopMax` · `desktopOnly` · `extDesktop` · `mobileMax` · `mobileOnly` · `mobileTablet`
|
|
161
|
+
|
|
162
|
+
- Breakpoint values come from `_config.scss` and stay in sync with any project overrides.
|
|
163
|
+
- Never hard-code `@media (min-width: 1040px)` or similar — always use `breakpoint()` so values stay consistent with config.
|
|
164
|
+
- Prefer media query breakpoints over window resize event listeners for responsive design whenever possible.
|
|
165
|
+
- Responsive styling should be mobile-first (`min-width` breakpoints) whenever possible. Use `mobileOnly`/`mobileMax` only for outlier cases that require a mobile-specific exception.
|
|
166
|
+
- In apps using SCSS, use the `breakpoint` mixin (`@include u.breakpoint(key)`). In apps using JS styling, use the `breakpoint` function (`breakpoint(key, styles)` or `media.key` for raw queries).
|
|
167
|
+
|
|
168
|
+
## Styling Expectations
|
|
169
|
+
|
|
170
|
+
- Components are built with SCSS modules and class-based variant hooks.
|
|
171
|
+
- If your app does not include this package's global CSS variable setup, visual output may differ.
|
|
172
|
+
- Agents should avoid hard-coding assumptions about token names beyond what the consumer app already defines.
|
|
173
|
+
|
|
174
|
+
## Custom Sizing / Scale Props
|
|
175
|
+
|
|
176
|
+
When a component accepts a numeric size/scale prop (for example `Button`'s `size` or `Toggle`'s `width`), the component sets **one** CSS custom property inline (only when the prop is provided), and the SCSS file does all the `calc()` math from there:
|
|
177
|
+
|
|
178
|
+
```tsx
|
|
179
|
+
// Component: only ever set the single scale variable
|
|
180
|
+
const sizeStyle = normalizedSize ? ({ "--btn-size": normalizedSize } as React.CSSProperties) : undefined;
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
```scss
|
|
184
|
+
// SCSS: give the variable a default, then derive every scaled property from it
|
|
185
|
+
%button-styles {
|
|
186
|
+
--btn-size: 1;
|
|
187
|
+
padding: calc(#{u.width(gutter, 0.5)} * var(--btn-size)) calc(#{u.width(gutter, 0.75)} * var(--btn-size));
|
|
188
|
+
border-radius: calc(#{u.width(gutter, 0.75)} * var(--btn-size));
|
|
189
|
+
font-size: max(10px, calc(#{u.width(text)} * var(--btn-size)));
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Do not compute `calc()` strings per-property in JS/TS (for example building a `padding: "calc(... * 1.5)"` string in the component). Always push that math into SCSS and only pass the raw scale/size value through a single CSS variable.
|
|
194
|
+
|
|
195
|
+
## Theme Authoring
|
|
196
|
+
|
|
197
|
+
- `theme.root` supports JS style objects and raw CSS/Sass strings.
|
|
198
|
+
- `theme.components.*` supports JS style objects and raw CSS/Sass strings.
|
|
199
|
+
- `theme.widths` accepts a partial record of width/breakpoint keys to override the default `--w-*` CSS variables. Values are numbers in `px`.
|
|
200
|
+
- String component styles are applied to the mapped target selector (for example `button`, `input[type='checkbox']`).
|
|
201
|
+
- For multi-file Sass workflows with syntax highlighting and mixins, import compiled CSS text via `*.scss?inline`.
|
|
202
|
+
|
|
203
|
+
Valid `theme.widths` keys: `"text" | "gutter" | "column" | "tablet" | "desktop" | "extDesktop" | "mobileMax" | "tabletMax" | "desktopMax"`
|
|
204
|
+
|
|
205
|
+
### Theme Authoring Usage Parameters
|
|
206
|
+
|
|
207
|
+
- Prefer authoring theme styles as SCSS over JS styling, except in apps that primarily use JS styling and do not use SCSS.
|
|
208
|
+
- Global component styles should use the `components` object.
|
|
209
|
+
- When declaring styles for multiple components globally for the app theme, use SCSS placeholders, mixins, or functions (in apps that use SCSS). For apps that use JS styling, use variables.
|
|
210
|
+
- Component styles should live in a `/components` subfolder within whatever folder contains the app theme declarations.
|
|
211
|
+
|
|
212
|
+
Example:
|
|
213
|
+
|
|
214
|
+
```tsx
|
|
215
|
+
import greenRoot from "./green.root.scss?inline";
|
|
216
|
+
import greenButton from "./green.button.scss?inline";
|
|
217
|
+
|
|
218
|
+
export const theme = {
|
|
219
|
+
widths: {
|
|
220
|
+
gutter: 20,
|
|
221
|
+
tablet: 768,
|
|
222
|
+
},
|
|
223
|
+
root: greenRoot,
|
|
224
|
+
components: {
|
|
225
|
+
button: greenButton,
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
```
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: craftsman-utility-functions
|
|
3
|
+
description: 'Utility function conventions for @stamcat/craftsman — the isEmpty validation helper and TypeScript/Sass parity for style utility patterns. Use when writing empty/falsy checks or referencing color()/width()/breakpoint() usage patterns for @stamcat/craftsman.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Craftsman Utility Functions
|
|
7
|
+
|
|
8
|
+
Non-styling utility function rules for AI agents generating code against `@stamcat/craftsman`. For package exports, hard rules, and component contracts, see the [craftsman-component-usage skill](../craftsman-component-usage/SKILL.md). For styling and theme authoring, see the [craftsman-style-utilities skill](../craftsman-style-utilities/SKILL.md).
|
|
9
|
+
|
|
10
|
+
## `isEmpty`
|
|
11
|
+
|
|
12
|
+
The package exports an `isEmpty` utility. **Always use it instead of writing inline empty checks.**
|
|
13
|
+
|
|
14
|
+
Import:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { isEmpty } from "@stamcat/craftsman/utilities/validations";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
It returns `true` for:
|
|
21
|
+
|
|
22
|
+
- `undefined`
|
|
23
|
+
- `null`
|
|
24
|
+
- empty objects — `{}`
|
|
25
|
+
- strings that are empty or whitespace-only — `""`, `" "`
|
|
26
|
+
- arrays with no elements — `[]`
|
|
27
|
+
|
|
28
|
+
Examples:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
// DO — use isEmpty
|
|
32
|
+
if (isEmpty(value)) { ... }
|
|
33
|
+
if (!isEmpty(items)) { ... }
|
|
34
|
+
|
|
35
|
+
// DO NOT — write these manually
|
|
36
|
+
if (value === undefined || value === null) { ... }
|
|
37
|
+
if (typeof value === "string" && value.trim().length === 0) { ... }
|
|
38
|
+
if (Object.keys(obj).length === 0) { ... }
|
|
39
|
+
if (arr.length === 0) { ... }
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Style utilities parity (`color`, `width`, `breakpoint`)
|
|
43
|
+
|
|
44
|
+
These utility patterns exist in both TypeScript and Sass. See the [craftsman-style-utilities skill](../craftsman-style-utilities/SKILL.md) for full usage details.
|
|
45
|
+
|
|
46
|
+
TypeScript usage:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { color, width, breakpoint } from "@stamcat/craftsman/styles";
|
|
50
|
+
|
|
51
|
+
const accent = color("blue500");
|
|
52
|
+
const alphaAccent = color("blue500", "rgba", 0.32);
|
|
53
|
+
const twoColumns = width("column", 2);
|
|
54
|
+
const mobileRule = breakpoint("mobileMax", "h4{font-size:14px;}");
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Sass usage (framework source):
|
|
58
|
+
|
|
59
|
+
```scss
|
|
60
|
+
@use "./src/styles/utilities" as u;
|
|
61
|
+
|
|
62
|
+
.example {
|
|
63
|
+
color: #{u.color(blue500)};
|
|
64
|
+
background: #{u.color(blue500, rgba, 0.32)};
|
|
65
|
+
max-width: #{u.width(column, 2)};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@include u.breakpoint(mobileMax) {
|
|
69
|
+
.example { font-size: #{u.width(text)}; }
|
|
70
|
+
}
|
|
71
|
+
```
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Accordion — Agent Usage Notes
|
|
2
|
+
|
|
3
|
+
Import:
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { Accordion } from "@stamcat/craftsman/Accordion";
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Props:
|
|
10
|
+
|
|
11
|
+
- `items: { id?: string; header: ReactNode; content: ReactNode; disabled?: boolean }[]` — required, data-driven like `Select`'s `options`.
|
|
12
|
+
- `multiple?: boolean` (default: `false`) — when `false`, opening an item closes any other open item.
|
|
13
|
+
- `expanded?: string[]` — controlled set of open item ids. Omit to let Accordion manage its own open/closed state.
|
|
14
|
+
- `defaultExpanded?: string[]` (default: `[]`) — initial open item ids when uncontrolled.
|
|
15
|
+
- `onChange?: (expanded: string[]) => void`
|
|
16
|
+
- Inherits all native `<div>` props (except `onChange`, which is overridden above).
|
|
17
|
+
|
|
18
|
+
Behavior notes:
|
|
19
|
+
|
|
20
|
+
- Each item renders as an `<h3>` heading wrapping a `<button>` trigger (`aria-expanded`, `aria-controls`) and a `role="region"` panel (`aria-labelledby`) — the WAI-ARIA Accordion pattern.
|
|
21
|
+
- Collapsed panels are hidden via the native `hidden` attribute (not just visually), so screen readers skip their content.
|
|
22
|
+
- The chevron icon is `FaAngleUp` from `react-icons/fa6`; it rotates 180deg when collapsed and un-rotates to point up when expanded — the same rotate-on-open treatment used for `Select`'s native dropdown arrow.
|
|
23
|
+
- Keyboard support mirrors a native `<select>`: `Tab` moves focus in/out, `Enter`/`Space` toggle the focused header (free via native `<button>` semantics), and `ArrowUp`/`ArrowDown`/`Home`/`End` move focus between headers without opening them.
|
|
24
|
+
- `disabled` items render a disabled `<button>` (`aria-disabled` is implied by the native `disabled` attribute) and cannot be toggled.
|
|
25
|
+
- If `id` is omitted per item, one is derived from `useId()` plus the item's index.
|
|
26
|
+
- Renders nothing (per `isEmpty`) if `items` is empty.
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
<Accordion
|
|
32
|
+
items={[
|
|
33
|
+
{ id: "shipping", header: "Shipping", content: <p>Ships in 3-5 business days.</p> },
|
|
34
|
+
{ id: "returns", header: "Returns", content: <p>30-day return window.</p> },
|
|
35
|
+
]}
|
|
36
|
+
/>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Controlled usage:
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
const [expanded, setExpanded] = useState<string[]>(["shipping"]);
|
|
43
|
+
|
|
44
|
+
<Accordion items={items} expanded={expanded} onChange={setExpanded} multiple />
|
|
45
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@layer craftsman-component{.accordion{border:1px solid var(--gray400);border-radius:calc(var(--w-gutter) * .5);flex-direction:column;display:flex;overflow:hidden}.accordion-item{border-bottom:1px solid var(--gray400)}.accordion-item:last-child{border-bottom:none}.accordion-item[data-disabled=true] .accordion-trigger{cursor:not-allowed;color:var(--gray400)}.accordion-item>.accordion-content{padding:calc(var(--w-gutter) * .75) var(--w-gutter)}.accordion-item>.accordion-content[hidden]{display:none}.accordion-trigger{justify-content:space-between;align-items:center;gap:calc(var(--w-gutter) * .5);width:100%;padding:calc(var(--w-gutter) * .75) var(--w-gutter);background-color:var(--white);text-align:left;color:inherit;border:none;display:flex}.accordion-trigger:focus-visible{outline:2px solid var(--blue500);outline-offset:-2px}.accordion-trigger span{flex-grow:1}.accordion-trigger svg{flex:none;transition:transform .2s;transform:rotate(180deg)}.accordion-item[data-expanded=true] .accordion-trigger svg{transform:rotate(0)}}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type AccordionItemType = {
|
|
2
|
+
id?: string;
|
|
3
|
+
header: React.ReactNode;
|
|
4
|
+
content: React.ReactNode;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
};
|
|
7
|
+
export type AccordionProps = Omit<React.ComponentPropsWithoutRef<"div">, "onChange"> & {
|
|
8
|
+
items: AccordionItemType[];
|
|
9
|
+
multiple?: boolean;
|
|
10
|
+
expanded?: string[];
|
|
11
|
+
defaultExpanded?: string[];
|
|
12
|
+
onChange?: (expanded: string[]) => void;
|
|
13
|
+
};
|
|
14
|
+
export declare const Accordion: React.FC<AccordionProps>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
@use "../../styles/utilities/_functions" as u;
|
|
2
|
+
|
|
3
|
+
@layer craftsman-component {
|
|
4
|
+
.accordion {
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
border: 1px solid var(--gray400);
|
|
8
|
+
border-radius: #{u.width("gutter", 0.5)};
|
|
9
|
+
overflow: hidden;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.accordion-item {
|
|
13
|
+
border-bottom: 1px solid var(--gray400);
|
|
14
|
+
|
|
15
|
+
&:last-child {
|
|
16
|
+
border-bottom: none;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
&[data-disabled="true"] .accordion-trigger {
|
|
20
|
+
cursor: not-allowed;
|
|
21
|
+
color: var(--gray400);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
> .accordion-content {
|
|
25
|
+
padding: #{u.width("gutter", 0.75)} #{u.width("gutter")};
|
|
26
|
+
|
|
27
|
+
&[hidden] {
|
|
28
|
+
display: none;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.accordion-trigger {
|
|
34
|
+
display: flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
justify-content: space-between;
|
|
37
|
+
gap: #{u.width("gutter", 0.5)};
|
|
38
|
+
width: 100%;
|
|
39
|
+
padding: #{u.width("gutter", 0.75)} #{u.width("gutter")};
|
|
40
|
+
background-color: var(--white);
|
|
41
|
+
border: none;
|
|
42
|
+
text-align: left;
|
|
43
|
+
color: inherit;
|
|
44
|
+
|
|
45
|
+
&:focus-visible {
|
|
46
|
+
outline: 2px solid var(--blue500);
|
|
47
|
+
outline-offset: -2px;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
span {
|
|
51
|
+
flex-grow: 1;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
svg {
|
|
55
|
+
flex: 0 0 auto;
|
|
56
|
+
transition: transform 200ms ease;
|
|
57
|
+
transform: rotate(180deg);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.accordion-item[data-expanded="true"] .accordion-trigger svg {
|
|
62
|
+
transform: rotate(0deg);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { isEmpty as e } from "../../utilities/validations.esm.js";
|
|
3
|
+
import './Accordion.css';/* empty css */
|
|
4
|
+
import { useId as t, useRef as n, useState as r } from "react";
|
|
5
|
+
import i from "clsx";
|
|
6
|
+
import { FaAngleUp as a } from "react-icons/fa6";
|
|
7
|
+
import { Fragment as o, jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
8
|
+
//#region src/components/Accordion/Accordion.tsx
|
|
9
|
+
var l = ({ items: l, multiple: u = !1, expanded: d, defaultExpanded: f = [], onChange: p, className: m, style: h, ...g }) => {
|
|
10
|
+
let _ = t(), v = n([]), [y, b] = r(f), x = d !== void 0, S = x ? d : y, C = (e, t) => e.id ?? `${_}-${t}`, w = (e) => {
|
|
11
|
+
x || b(e), p?.(e);
|
|
12
|
+
}, T = (e) => {
|
|
13
|
+
let t = l.length, n = (e % t + t) % t;
|
|
14
|
+
v.current[n]?.focus();
|
|
15
|
+
}, E = (e) => {
|
|
16
|
+
let t = S.includes(e);
|
|
17
|
+
w(u ? t ? S.filter((t) => t !== e) : [...S, e] : t ? [] : [e]);
|
|
18
|
+
}, D = (e, t) => {
|
|
19
|
+
switch (e.key) {
|
|
20
|
+
case "ArrowDown":
|
|
21
|
+
e.preventDefault(), T(t + 1);
|
|
22
|
+
break;
|
|
23
|
+
case "ArrowUp":
|
|
24
|
+
e.preventDefault(), T(t - 1);
|
|
25
|
+
break;
|
|
26
|
+
case "Home":
|
|
27
|
+
e.preventDefault(), T(0);
|
|
28
|
+
break;
|
|
29
|
+
case "End":
|
|
30
|
+
e.preventDefault(), T(l.length - 1);
|
|
31
|
+
break;
|
|
32
|
+
default: break;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
return e(l) ? /* @__PURE__ */ s(o, {}) : /* @__PURE__ */ s("div", {
|
|
36
|
+
className: i("accordion", m),
|
|
37
|
+
style: h,
|
|
38
|
+
...g,
|
|
39
|
+
children: l.map((e, t) => {
|
|
40
|
+
let n = C(e, t), r = `${n}-header`, i = `${n}-panel`, l = S.includes(n);
|
|
41
|
+
return /* @__PURE__ */ c("div", {
|
|
42
|
+
className: "accordion-item",
|
|
43
|
+
"data-expanded": l,
|
|
44
|
+
"data-disabled": e.disabled,
|
|
45
|
+
children: [/* @__PURE__ */ s("header", { children: /* @__PURE__ */ c("button", {
|
|
46
|
+
type: "button",
|
|
47
|
+
id: r,
|
|
48
|
+
ref: (e) => {
|
|
49
|
+
v.current[t] = e;
|
|
50
|
+
},
|
|
51
|
+
className: "accordion-trigger",
|
|
52
|
+
"aria-expanded": l,
|
|
53
|
+
"aria-controls": i,
|
|
54
|
+
disabled: e.disabled,
|
|
55
|
+
onClick: () => E(n),
|
|
56
|
+
onKeyDown: (e) => D(e, t),
|
|
57
|
+
children: [/* @__PURE__ */ s(o, { children: e.header }), /* @__PURE__ */ s(a, { "aria-hidden": "true" })]
|
|
58
|
+
}) }), /* @__PURE__ */ s("div", {
|
|
59
|
+
id: i,
|
|
60
|
+
className: "accordion-content",
|
|
61
|
+
"aria-labelledby": r,
|
|
62
|
+
hidden: !l,
|
|
63
|
+
children: e.content
|
|
64
|
+
})]
|
|
65
|
+
}, n);
|
|
66
|
+
})
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
//#endregion
|
|
70
|
+
export { l as Accordion };
|
|
@@ -2,67 +2,67 @@
|
|
|
2
2
|
import { Button as e } from "../Button/Button.esm.js";
|
|
3
3
|
import { Pagination as t } from "../Pagination/Pagination2.esm.js";
|
|
4
4
|
import './Carousel.css';/* empty css */
|
|
5
|
-
import n from "
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
5
|
+
import { useCallback as n, useSyncExternalStore as r } from "react";
|
|
6
|
+
import i from "clsx";
|
|
7
|
+
import { FaAngleLeft as a, FaAngleRight as o, FaCircle as s, FaRegCircle as c } from "react-icons/fa6";
|
|
8
|
+
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
9
9
|
import d from "embla-carousel-react";
|
|
10
10
|
//#region src/components/Carousel/Carousel.tsx
|
|
11
11
|
var f = (f) => {
|
|
12
|
-
let { slides: p = [], className: m, style: h, buttons: g = !0, pagination: _ = "dots", ...v } = f, [y, b] = d(v), x =
|
|
12
|
+
let { slides: p = [], className: m, style: h, buttons: g = !0, pagination: _ = "dots", ...v } = f, [y, b] = d(v), x = n((e) => b ? (b.on("select", e).on("reInit", e), () => {
|
|
13
13
|
b.off("select", e), b.off("reInit", e);
|
|
14
|
-
}) : () => {}, [b]), S =
|
|
14
|
+
}) : () => {}, [b]), S = r(x, () => !!b?.canScrollPrev(), () => !1), C = r(x, () => !!b?.canScrollNext(), () => !1), w = r(x, () => b?.selectedScrollSnap() ?? 0, () => 0), T = () => b?.scrollPrev(), E = () => b?.scrollNext(), D = (e) => {
|
|
15
15
|
let t = parseInt(e.currentTarget.value, 10) || 0;
|
|
16
16
|
b?.scrollTo(t);
|
|
17
17
|
};
|
|
18
|
-
return /* @__PURE__ */
|
|
19
|
-
className:
|
|
18
|
+
return /* @__PURE__ */ u("div", {
|
|
19
|
+
className: i("carousel", m),
|
|
20
20
|
style: h,
|
|
21
|
-
children: [/* @__PURE__ */
|
|
21
|
+
children: [/* @__PURE__ */ l("div", {
|
|
22
22
|
className: "carousel__viewport",
|
|
23
23
|
ref: y,
|
|
24
|
-
children: /* @__PURE__ */
|
|
24
|
+
children: /* @__PURE__ */ l("div", {
|
|
25
25
|
className: "carousel__container",
|
|
26
|
-
children: p.map((e, t) => /* @__PURE__ */
|
|
26
|
+
children: p.map((e, t) => /* @__PURE__ */ l("div", {
|
|
27
27
|
className: "carousel__slide",
|
|
28
28
|
children: e
|
|
29
29
|
}, `slide-${t}`))
|
|
30
30
|
})
|
|
31
|
-
}), /* @__PURE__ */
|
|
31
|
+
}), /* @__PURE__ */ u("div", {
|
|
32
32
|
className: "carousel__controls",
|
|
33
33
|
children: [
|
|
34
|
-
g && /* @__PURE__ */
|
|
34
|
+
g && /* @__PURE__ */ u("div", {
|
|
35
35
|
className: "carousel__buttons",
|
|
36
|
-
children: [/* @__PURE__ */
|
|
36
|
+
children: [/* @__PURE__ */ l(e, {
|
|
37
37
|
variant: "text",
|
|
38
38
|
onClick: T,
|
|
39
39
|
disabled: !S,
|
|
40
40
|
name: "prev",
|
|
41
|
-
children: /* @__PURE__ */
|
|
41
|
+
children: /* @__PURE__ */ l(a, {
|
|
42
42
|
size: 24,
|
|
43
43
|
"aria-label": "prev"
|
|
44
44
|
})
|
|
45
|
-
}), /* @__PURE__ */
|
|
45
|
+
}), /* @__PURE__ */ l(e, {
|
|
46
46
|
variant: "text",
|
|
47
47
|
onClick: E,
|
|
48
48
|
disabled: !C,
|
|
49
49
|
name: "next",
|
|
50
|
-
children: /* @__PURE__ */
|
|
50
|
+
children: /* @__PURE__ */ l(o, {
|
|
51
51
|
size: 24,
|
|
52
52
|
"aria-label": "next"
|
|
53
53
|
})
|
|
54
54
|
})]
|
|
55
55
|
}),
|
|
56
|
-
_ === "dots" && /* @__PURE__ */
|
|
56
|
+
_ === "dots" && /* @__PURE__ */ l("div", {
|
|
57
57
|
className: "carousel__dots",
|
|
58
|
-
children: p.map((t, n) => /* @__PURE__ */
|
|
58
|
+
children: p.map((t, n) => /* @__PURE__ */ l(e, {
|
|
59
59
|
variant: "text",
|
|
60
60
|
value: n,
|
|
61
61
|
onClick: D,
|
|
62
|
-
children:
|
|
62
|
+
children: l(w === n ? s : c, { size: 16 })
|
|
63
63
|
}, `dot-${n}`))
|
|
64
64
|
}),
|
|
65
|
-
_ === "numbers" && /* @__PURE__ */
|
|
65
|
+
_ === "numbers" && /* @__PURE__ */ l(t, {
|
|
66
66
|
total: p.length,
|
|
67
67
|
current: w,
|
|
68
68
|
onChange: D
|
|
@@ -1 +1 @@
|
|
|
1
|
-
@layer craftsman-base{.react-date-picker__wrapper
|
|
1
|
+
@layer craftsman-base{.react-date-picker__wrapper{color:var(--text);border:1px solid var(--gray400);padding:calc(var(--w-gutter) * .5);background-color:var(--white);border-radius:6px}.react-date-picker__wrapper:focus{box-shadow:none;outline:0}.react-date-picker__wrapper:hover{border-color:var(--gray500)}[type=number].react-date-picker__wrapper::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}[type=number].react-date-picker__wrapper::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}[type=number].react-date-picker__wrapper{appearance:textfield}.react-date-picker__wrapper:disabled,.disabled.react-date-picker__wrapper{border-color:var(--gray400);background-color:var(--gray100)}.react-date-picker__calendar{z-index:1;width:350px;max-width:100vw}.react-date-picker__calendar .react-calendar{border-width:thin}.react-date-picker__calendar--closed,.react-date-picker__clock--closed{display:none}.react-date-picker__calendar .react-date-picker__inputGroup__input:invalid{background:unset}}.react-date-picker,.react-date-picker *,.react-date-picker :before,.react-date-picker :after{box-sizing:border-box}@layer craftsman-component{.react-date-picker{display:inline-flex;position:relative}.react-date-picker--disabled{color:#6d6d6d;background-color:#f0f0f0}.react-date-picker__wrapper{flex-grow:1;flex-shrink:0;padding:6px 8px;display:flex}.react-date-picker__inputGroup{box-sizing:content-box;flex-grow:1;padding:0 2px}.react-date-picker__inputGroup__divider{white-space:pre;padding:1px 0}.react-date-picker__inputGroup__divider,.react-date-picker__inputGroup__leadingZero{font:inherit;display:inline-block}.react-date-picker__inputGroup__input{text-align:center;color:currentColor;min-width:calc(2ch + 1px);height:calc(100% - 2px);font:inherit;box-sizing:content-box;appearance:textfield;background:0 0;border:0;padding:1px;position:relative}[name=year].react-date-picker__inputGroup__input{min-width:calc(4ch + 2px)}.react-date-picker__inputGroup__input:disabled{background-color:unset}.react-date-picker__inputGroup__input::-webkit-outer-spin-button{appearance:none;margin:0}.react-date-picker__inputGroup__input::-webkit-inner-spin-button{appearance:none;margin:0}.react-date-picker__inputGroup__input--hasLeadingZero{margin-left:-.54em;padding-left:calc(1px + .54em)}.react-date-picker__button{border:0;padding:4px 4px 4px 7px}.react-date-picker__button:hover{background:0 0}}
|
|
@@ -2,25 +2,25 @@
|
|
|
2
2
|
import { InputWrapper as e } from "../Input/InputWrapper.esm.js";
|
|
3
3
|
import './ReactCalendar.css';import './DatePicker.css';/* empty css */
|
|
4
4
|
/* empty css */
|
|
5
|
-
import t from "
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
5
|
+
import { useId as t } from "react";
|
|
6
|
+
import n from "clsx";
|
|
7
|
+
import { FaRegCalendar as r, FaX as i } from "react-icons/fa6";
|
|
8
|
+
import { jsx as a } from "react/jsx-runtime";
|
|
9
|
+
import { DatePicker as o } from "react-date-picker";
|
|
10
10
|
//#region src/components/DatePicker/DatePicker.tsx
|
|
11
11
|
var s = ({ label: s, id: c, labelPosition: l = "top", required: u = !1, error: d, style: f, className: p, ...m }) => {
|
|
12
|
-
let h =
|
|
13
|
-
return /* @__PURE__ */
|
|
12
|
+
let h = t();
|
|
13
|
+
return /* @__PURE__ */ a(e, {
|
|
14
14
|
id: c || h,
|
|
15
|
-
className:
|
|
15
|
+
className: n("datePicker", p),
|
|
16
16
|
label: s,
|
|
17
17
|
labelPosition: l,
|
|
18
18
|
error: d,
|
|
19
19
|
required: u,
|
|
20
20
|
style: f,
|
|
21
|
-
children: /* @__PURE__ */
|
|
22
|
-
calendarIcon: /* @__PURE__ */
|
|
23
|
-
clearIcon: /* @__PURE__ */
|
|
21
|
+
children: /* @__PURE__ */ a(o, {
|
|
22
|
+
calendarIcon: /* @__PURE__ */ a(r, { size: 16 }),
|
|
23
|
+
clearIcon: /* @__PURE__ */ a(i, { size: 14 }),
|
|
24
24
|
...m
|
|
25
25
|
})
|
|
26
26
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
@layer craftsman-base{.react-daterange-picker__wrapper
|
|
1
|
+
@layer craftsman-base{.react-daterange-picker__wrapper{color:var(--text);border:1px solid var(--gray400);padding:calc(var(--w-gutter) * .5);background-color:var(--white);border-radius:6px}.react-daterange-picker__wrapper:focus{box-shadow:none;outline:0}.react-daterange-picker__wrapper:hover{border-color:var(--gray500)}[type=number].react-daterange-picker__wrapper::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}[type=number].react-daterange-picker__wrapper::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}[type=number].react-daterange-picker__wrapper{appearance:textfield}.react-daterange-picker__wrapper:disabled,.disabled.react-daterange-picker__wrapper{border-color:var(--gray400);background-color:var(--gray100)}}.react-daterange-picker,.react-daterange-picker *,.react-daterange-picker :before,.react-daterange-picker :after{box-sizing:border-box}@layer craftsman-component{.react-daterange-picker{display:inline-flex;position:relative}.react-daterange-picker--disabled{color:#6d6d6d;background-color:#f0f0f0}.react-daterange-picker__wrapper{flex-grow:1;flex-shrink:0;padding:6px 8px;display:flex}.react-daterange-picker__inputGroup{box-sizing:content-box;flex-grow:1;padding:0 2px}.react-daterange-picker__inputGroup__divider{white-space:pre;padding:1px 0}.react-daterange-picker__inputGroup__divider,.react-daterange-picker__inputGroup__leadingZero{font:inherit;display:inline-block}.react-daterange-picker__inputGroup__input{text-align:center;color:currentColor;min-width:calc(2ch + 1px);height:calc(100% - 2px);font:inherit;box-sizing:content-box;appearance:textfield;background:0 0;border:0;padding:1px;position:relative}[name=year].react-daterange-picker__inputGroup__input{min-width:calc(4ch + 2px)}.react-daterange-picker__inputGroup__input:disabled{background-color:unset}.react-daterange-picker__inputGroup__input::-webkit-outer-spin-button{appearance:none;margin:0}.react-daterange-picker__inputGroup__input::-webkit-inner-spin-button{appearance:none;margin:0}.react-daterange-picker__inputGroup__input--hasLeadingZero{margin-left:-.54em;padding-left:calc(1px + .54em)}.react-daterange-picker__button{border:0;padding:4px 4px 4px 7px}.react-daterange-picker__button:hover{background:0 0}.react-daterange-picker__calendar{z-index:1;width:350px;max-width:100vw}.react-daterange-picker__calendar .react-calendar{border-width:thin}.react-daterange-picker__calendar--closed,.react-daterange-picker__clock--closed{display:none}.react-daterange-picker__calendar .react-daterange-picker__inputGroup__input:invalid{background:unset}}
|