@stamcat/craftsman 0.0.23-alpha.10 → 0.0.23-alpha.12

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 CHANGED
@@ -12,6 +12,7 @@ Use these imports:
12
12
  import { Button } from "@stamcat/craftsman/Button";
13
13
  import { Input } from "@stamcat/craftsman/Input";
14
14
  import { Loader } from "@stamcat/craftsman/Loader";
15
+ import { Modal } from "@stamcat/craftsman/Modal";
15
16
  ```
16
17
 
17
18
  Do not assume a root export like `@stamcat/craftsman` unless that export is explicitly added to package `exports`.
@@ -37,12 +38,25 @@ Props:
37
38
 
38
39
  - Inherits all native `<button>` props.
39
40
  - `variant?: "primary" | "default" | "text"` (default: `"default"`)
41
+ - `size?: number` (default visual scale is `1`)
40
42
  - `styles?: SerializedStyles` (Emotion override)
41
43
 
42
44
  Behavior notes:
43
45
 
44
46
  - `type` defaults to `"button"`.
45
47
  - For `variant !== "default"`, variant is appended to `className` (for example `"primary"`).
48
+ - Theme class merge: if `theme.components.button` is a string, it is appended to the class list.
49
+ - `className` is preserved and merged after variant/theme classes.
50
+ - If `children` is empty (per `isEmpty`), the component renders nothing.
51
+ - `size` is clamped to `[0.1, 10]` before styling is applied.
52
+ - When `size` is provided, Button scales:
53
+ - `border-radius: calc(var(--btn-border-radius) * size)`
54
+ - `padding: calc(var(--btn-pad-y) * size) calc(var(--btn-pad-x) * size)`
55
+ - `font-size: max(10px, calc(var(--w-text) * size))`
56
+ - There is no dedicated icon/loading prop. Icons, loaders, and mixed content are passed as `children`.
57
+ - Supports both real disable (`disabled={true}`) and style-only disable (`className="disabled"`).
58
+ - Native button modes are supported (`type="button" | "submit" | "reset"`).
59
+ - Accessibility props such as `aria-label` pass through unchanged.
46
60
 
47
61
  Example:
48
62
 
@@ -50,6 +64,33 @@ Example:
50
64
  <Button variant="primary" onClick={onSave}>Save</Button>
51
65
  ```
52
66
 
67
+ Story-aligned usage examples:
68
+
69
+ ```tsx
70
+ // Scaled compact button (size is clamped to [0.1, 10])
71
+ <Button variant="primary" size={0.5}>Mini Action</Button>
72
+
73
+ // Icon content
74
+ <Button variant="primary">
75
+ <TruckIcon size={20} /> <span>Ship</span>
76
+ </Button>
77
+
78
+ // Loading content
79
+ <Button aria-label="Saving">
80
+ <Loader type="boxy" width={32} color="#de13ca" />
81
+ </Button>
82
+
83
+ // Style-only disabled appearance while still allowing click handlers
84
+ <Button className="disabled" onClick={onClick}>Disabled Look</Button>
85
+
86
+ // Native submit behavior
87
+ <Button type="submit" aria-label="Save form">Save</Button>
88
+ ```
89
+
90
+ Implementation caution:
91
+
92
+ - Size scaling depends on CSS variables provided by the package global styles (`--btn-border-radius`, `--btn-pad-y`, `--btn-pad-x`, `--w-text`).
93
+
53
94
  ### Input
54
95
 
55
96
  Import:
@@ -68,6 +109,54 @@ Example:
68
109
  <Input type="email" placeholder="you@company.com" required />
69
110
  ```
70
111
 
112
+ ### Modal
113
+
114
+ Import:
115
+
116
+ ```tsx
117
+ import { Modal } from "@stamcat/craftsman/Modal";
118
+ ```
119
+
120
+ Props:
121
+
122
+ - Inherits all native `<div>` props.
123
+ - `visible?: boolean` (when falsy, component renders nothing)
124
+ - `onDismiss?: () => void`
125
+ - `type?: "dialog" | "panel"` (default: `"dialog"`)
126
+ - `header?: string | React.ReactNode`
127
+ - `backgroundDismiss?: boolean` (default behavior: `true`)
128
+ - `hideDismissIcon?: boolean` (default behavior: close icon is shown)
129
+ - `footer?: React.ReactNode`
130
+ - `styles?: SerializedStyles` (applies to outer modal wrapper)
131
+
132
+ Behavior notes:
133
+
134
+ - Modal is controlled; parent owns open/close state via `visible` and `onDismiss`.
135
+ - Close icon and background click both dismiss through `onDismiss`.
136
+ - Background dismiss only runs when `backgroundDismiss` is `true` or `undefined`.
137
+ - Dismiss uses a short close animation before calling `onDismiss` (~280ms timeout).
138
+ - `type="dialog"` renders centered responsive dialog sizing; `type="panel"` renders a right-side panel.
139
+ - `footer` is rendered in an action row container below modal content.
140
+
141
+ Example:
142
+
143
+ ```tsx
144
+ const [open, setOpen] = useState(false);
145
+
146
+ <>
147
+ <Button variant="primary" onClick={() => setOpen(true)}>Open</Button>
148
+ <Modal
149
+ visible={open}
150
+ onDismiss={() => setOpen(false)}
151
+ type="dialog"
152
+ header="Confirm Action"
153
+ footer={<><Button onClick={() => setOpen(false)}>Cancel</Button><Button variant="primary">Confirm</Button></>}
154
+ >
155
+ <p>Are you sure?</p>
156
+ </Modal>
157
+ </>
158
+ ```
159
+
71
160
  ### Loader
72
161
 
73
162
  Import:
@@ -151,12 +240,12 @@ if (arr.length === 0) { ... }
151
240
 
152
241
  1. README is minimal; treat this guide as the source of truth for agent usage.
153
242
  2. Theme utilities exist in source but are not guaranteed public package exports.
154
- 3. Additional components in source (for example `Progress`) may not yet be exported.
243
+ 3. `Progress` exists in source but is incomplete and intentionally omitted from this guide for now.
155
244
 
156
245
  ## Safe Fallback Strategy for Agents
157
246
 
158
247
  If uncertain about available exports:
159
248
 
160
- 1. Use only `Button`, `Input`, and `Loader` from their component entry points.
249
+ 1. Use only `Button`, `Input`, `Loader`, and `Modal` from their component entry points.
161
250
  2. Do not invent package APIs.
162
251
  3. Prefer native HTML elements for anything not explicitly exported.
@@ -2,6 +2,7 @@ import { SerializedStyles } from '@emotion/react';
2
2
  import { ButtonType } from '../styles/global/components/button';
3
3
  export type ButtonProps = React.ComponentProps<"button"> & {
4
4
  variant?: ButtonType;
5
+ size?: number;
5
6
  styles?: SerializedStyles;
6
7
  };
7
8
  export declare const Button: React.FC<ButtonProps>;
@@ -18,6 +18,6 @@ export declare class Modal extends React.PureComponent<ModalProps, ModalState> {
18
18
  readonly state: ModalState;
19
19
  onDismiss: () => void;
20
20
  onClickBackground: () => void;
21
- render(): React.JSX.Element | null;
21
+ render(): React.JSX.Element;
22
22
  }
23
23
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stamcat/craftsman",
3
- "version": "0.0.23-alpha.10",
3
+ "version": "0.0.23-alpha.12",
4
4
  "type": "module",
5
5
  "description": "A powerful, lightweight framework for design systems",
6
6
  "repository": {
@@ -16,8 +16,8 @@
16
16
  "bugs": {
17
17
  "url": "https://github.com/Stamcat/craftsman/issues"
18
18
  },
19
- "types": "index.d.ts",
20
- "main": "index.js",
19
+ "types": "./main.d.ts",
20
+ "main": "./Components.esm.js",
21
21
  "homepage": "https://github.com/Stamcat/craftsman#readme",
22
22
  "scripts": {
23
23
  "prepublishOnly": "node ./scripts/prepublishCheck.js"
@@ -39,21 +39,25 @@
39
39
  },
40
40
  "devDependencies": {},
41
41
  "exports": {
42
+ ".": {
43
+ "types": "./main.d.ts",
44
+ "default": "./Components.esm.js"
45
+ },
42
46
  "./Button": {
43
- "types": "./Button.d.ts",
44
- "default": "./Button.es.js"
47
+ "types": "./components/Button.d.ts",
48
+ "default": "./src/components/Button.esm.js"
45
49
  },
46
50
  "./Input": {
47
- "types": "./Input.d.ts",
48
- "default": "./Input.es.js"
51
+ "types": "./components/Input.d.ts",
52
+ "default": "./src/components/Input.esm.js"
49
53
  },
50
54
  "./Loader": {
51
- "types": "./Loader.d.ts",
52
- "default": "./Loader.es.js"
55
+ "types": "./components/Loader.d.ts",
56
+ "default": "./src/components/Loader.esm.js"
53
57
  },
54
58
  "./Modal": {
55
- "types": "./Modal.d.ts",
56
- "default": "./Modal.es.js"
59
+ "types": "./components/Modal.d.ts",
60
+ "default": "./src/components/Modal.esm.js"
57
61
  }
58
62
  }
59
63
  }
@@ -1,20 +1,27 @@
1
- import { useTheme as e } from "@emotion/react";
2
- import t from "@emotion/styled";
3
- import { jsx as n } from "react/jsx-runtime";
1
+ import { isEmpty as e } from "../utilities/validations.esm.js";
2
+ import { css as t, useTheme as n } from "@emotion/react";
3
+ import r from "@emotion/styled";
4
+ import { Fragment as i, jsx as a } from "react/jsx-runtime";
4
5
  //#region src/components/Button.tsx
5
- var r = t.button`
6
+ var o = r.button`
6
7
  ${(e) => e.styles}
7
- `, i = (t) => {
8
- let { type: i = "button", variant: a = "default", className: o, ...s } = t, c = e(), l = typeof c?.components?.button == "string" ? c.components.button : void 0;
9
- return /* @__PURE__ */ n(r, {
10
- type: i,
11
- className: [
12
- a === "default" ? void 0 : a,
13
- l,
14
- o
15
- ].filter(Boolean).join(" ") || void 0,
16
- ...s
8
+ ${((e) => e.size && t`
9
+ border-radius: calc(var(--btn-border-radius) * ${e.size});
10
+ padding: calc(var(--btn-pad-y) * ${e.size}) calc(var(--btn-pad-x) * ${e.size});
11
+ font-size: max(10px, calc(var(--w-text) * ${e.size}));
12
+ `)}
13
+ `, s = (t) => {
14
+ let { type: r = "button", variant: s = "default", className: c, size: l, ...u } = t, d = typeof l == "number" ? Math.min(10, Math.max(.1, l)) : void 0, f = n(), p = typeof f?.components?.button == "string" ? f.components.button : void 0, m = [
15
+ s === "default" ? void 0 : s,
16
+ p,
17
+ c
18
+ ].filter(Boolean).join(" ") || void 0;
19
+ return e(t.children) ? /* @__PURE__ */ a(i, {}) : /* @__PURE__ */ a(o, {
20
+ type: r,
21
+ size: d,
22
+ className: m,
23
+ ...u
17
24
  });
18
25
  };
19
26
  //#endregion
20
- export { i as Button };
27
+ export { s as Button };
@@ -104,7 +104,6 @@ var d = a`
104
104
  }
105
105
  `, C = i`
106
106
  text-decoration: none;
107
- // padding: 0 calc(var(--w-gutter) * 0.25) 0 var(--w-gutter);
108
107
  height: 36px;
109
108
  width: 36px;
110
109
  border-radius: 50%;
@@ -122,8 +121,7 @@ var d = a`
122
121
  }, 280));
123
122
  };
124
123
  onClickBackground = () => {
125
- let e = this.props.backgroundDismiss;
126
- (e === !0 || e === void 0) && this.onDismiss();
124
+ (this.props.backgroundDismiss || !0) === !0 && this.onDismiss();
127
125
  };
128
126
  render() {
129
127
  return this.props.visible ? /* @__PURE__ */ l(h, {
@@ -149,7 +147,7 @@ var d = a`
149
147
  isClosing: this.state.isClosing,
150
148
  onClick: this.onClickBackground
151
149
  })]
152
- }) : null;
150
+ }) : /* @__PURE__ */ c(s, {});
153
151
  }
154
152
  };
155
153
  //#endregion
@@ -66,7 +66,7 @@ var e = {
66
66
  purple800: "#6b21a8",
67
67
  purple900: "#581c87"
68
68
  }, t = {
69
- text: 16,
69
+ text: 14,
70
70
  column: 60,
71
71
  gutter: 16,
72
72
  extDesktop: 1320,
@@ -0,0 +1,7 @@
1
+ //#region src/utilities/validations.ts
2
+ /* #__PURE__ */
3
+ function e(e) {
4
+ return e == null || typeof e == "object" && Object.keys(e).length === 0 || typeof e == "string" && e.trim().length === 0 || typeof e == "string" && e === " ";
5
+ }
6
+ //#endregion
7
+ export { e as isEmpty };
@@ -1,15 +0,0 @@
1
- import { Meta, StoryObj } from '@storybook/react-vite';
2
- import { Button } from '../components/Button';
3
- declare const meta: Meta<typeof Button>;
4
- export default meta;
5
- type Story = StoryObj<typeof meta>;
6
- export declare const Default: Story;
7
- export declare const Primary: Story;
8
- export declare const Text: Story;
9
- export declare const Disabled: Story;
10
- export declare const FullyDisabled: Story;
11
- export declare const Loading: Story;
12
- export declare const AsSubmit: Story;
13
- export declare const WithClassName: Story;
14
- export declare const WithAriaLabel: Story;
15
- export declare const WithEmotionStyles: Story;
@@ -1,5 +0,0 @@
1
- import { Meta, StoryObj } from '@storybook/react-vite';
2
- declare const meta: Meta;
3
- export default meta;
4
- type Story = StoryObj<typeof meta>;
5
- export declare const Palette: Story;
@@ -1,36 +0,0 @@
1
- import { Meta, StoryObj } from '@storybook/react-vite';
2
- declare const meta: Meta;
3
- export default meta;
4
- type Story = StoryObj<typeof meta>;
5
- export declare const AntDesign: Story;
6
- export declare const BoxIcons: Story;
7
- export declare const BootstrapIcons: Story;
8
- export declare const CssGgIcons: Story;
9
- export declare const CircumIcons: Story;
10
- export declare const Devicons: Story;
11
- export declare const FontAwesome: Story;
12
- export declare const FontAwesome6: Story;
13
- export declare const FlatColorIcons: Story;
14
- export declare const Feather: Story;
15
- export declare const GameIcons: Story;
16
- export declare const GithubOcticons: Story;
17
- export declare const GrommetIcons: Story;
18
- export declare const HeroIcons: Story;
19
- export declare const HeroIcons2: Story;
20
- export declare const IcoMoonFree: Story;
21
- export declare const Ionicons4: Story;
22
- export declare const Ionicons5: Story;
23
- export declare const LineAwesome: Story;
24
- export declare const Lucide: Story;
25
- export declare const MaterialDesignIcons: Story;
26
- export declare const PhosphorIcons: Story;
27
- export declare const RemixIcons: Story;
28
- export declare const RadixIcons: Story;
29
- export declare const SimpleIcons: Story;
30
- export declare const SimpleLineIcons: Story;
31
- export declare const TablerIcons: Story;
32
- export declare const ThemifyIcons: Story;
33
- export declare const Typicons: Story;
34
- export declare const VSCodeIcons: Story;
35
- export declare const WeatherIcons: Story;
36
- export declare const Palette: Story;
@@ -1,6 +0,0 @@
1
- import { Meta, StoryObj } from '@storybook/react';
2
- import { Loader } from '../components/Loader';
3
- declare const meta: Meta<typeof Loader>;
4
- export default meta;
5
- type Story = StoryObj<typeof Loader>;
6
- export declare const Basic: Story;
@@ -1,8 +0,0 @@
1
- import { Meta, StoryObj } from '@storybook/react-vite';
2
- import { Modal } from '../components/Modal';
3
- declare const meta: Meta<typeof Modal>;
4
- export default meta;
5
- type Story = StoryObj<typeof meta>;
6
- export declare const Dialog: Story;
7
- export declare const Panel: Story;
8
- export declare const NonDismissableBackground: Story;
@@ -1,7 +0,0 @@
1
- import { Meta, StoryObj } from '@storybook/react-vite';
2
- import { ToastContainerProps } from 'react-toastify';
3
- type StoryArgs = Pick<ToastContainerProps, "position" | "autoClose" | "theme" | "closeOnClick" | "pauseOnHover" | "draggable" | "newestOnTop">;
4
- declare const meta: Meta<StoryArgs>;
5
- export default meta;
6
- type Story = StoryObj<StoryArgs>;
7
- export declare const UsageGuide: Story;
@@ -1,6 +0,0 @@
1
- import { Meta, StoryObj } from '@storybook/react-vite';
2
- declare const meta: Meta;
3
- export default meta;
4
- type Story = StoryObj<typeof meta>;
5
- export declare const ScaleAndSemantics: Story;
6
- export declare const LongFormArticle: Story;