@base-framework/ui 1.0.2030 → 1.0.2031

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.
Files changed (2) hide show
  1. package/copilot.md +388 -223
  2. package/package.json +1 -1
package/copilot.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  This repo is a UI component library for the Base Framework, organized with Atomic Design (atoms → molecules → organisms → pages/templates) and styled with Tailwind CSS v4. It builds to multiple entry points via Vite.
4
4
 
5
+ **📚 Full documentation available in `ui.wiki/` folder - refer to it for comprehensive guides.**
6
+
7
+ ## CRITICAL: Before You Start
8
+ This is NOT React, Vue, or standard JSX. This uses Base Framework's declarative atoms/components with specific syntax patterns. **Read the documentation in base.wiki/ and the icon/children sections below carefully to avoid common mistakes.**
9
+
10
+ ### Key Differences from React/Vue
11
+ 1. **Children as second argument** - NEVER in props
12
+ 2. **Icons passed as children** - Not as icon prop in Icon component
13
+ 3. **Lists use map/for props** - Not regular .map()
14
+ 4. **Data binding with bind** - Not value + onChange
15
+ 5. **Reactive Data object** - Not useState
16
+
5
17
  ## How things fit together
6
18
  - Runtime primitives come from external packages:
7
19
  - `@base-framework/base` supplies Component, Atom, Data, Jot, Events, router, NavLink, etc.
@@ -34,15 +46,210 @@ This repo is a UI component library for the Base Framework, organized with Atomi
34
46
  - Subscriptions: `On('key', callback)` or `OnState/Open` utilities to react to state.
35
47
  - Parent context: `UseParent(({ state, ... }) => ...)` to access parent component refs.
36
48
 
37
- ### Important: Atom argument patterns
38
- Atoms created with `Atom()` support flexible argument patterns:
49
+ ### CRITICAL: Atom argument patterns
50
+ Atoms support flexible argument patterns. Children MUST be passed as second argument when it's an array:
39
51
  - **Props only**: `Div({ class: 'text' })`
40
52
  - **Text child only**: `Div('test')`
41
53
  - **Array children only**: `Div([Div('test')])`
42
54
  - **Props and text**: `Div({ class: 'text' }, 'test')`
43
- - **Props and array children**: `Div({ class: 'text' }, [Div('test')])`
55
+ - **Props and array children**: `Div({ class: 'text' }, [Div('test'), Div('test2')])`
56
+
57
+ ❌ WRONG: `Div({ class: 'text', children: [...] })` - Never pass children in props
58
+ ✅ CORRECT: `Div({ class: 'text' }, [...])`
59
+
60
+ ## Component Types and When to Use
61
+
62
+ ### Use Atom for:
63
+ - Stateless UI elements (buttons, badges, icons, labels)
64
+ - Simple compositions of other atoms
65
+ - Variants of existing atoms
66
+ - Visual-only components without internal state
67
+
68
+ ```javascript
69
+ export const Badge = Atom((props, children) => (
70
+ Span({
71
+ ...props,
72
+ class: `inline-flex rounded-full px-2.5 py-0.5 ${props.class || ''}`
73
+ }, children)
74
+ ));
75
+ ```
76
+
77
+ ### Use Component for:
78
+ - Components with internal state (Data or setupStates)
79
+ - Components that need lifecycle methods
80
+ - Complex interactions requiring methods
81
+ - Organisms like DataTable, Calendar, TabGroup
82
+
83
+ ```javascript
84
+ export class Counter extends Component {
85
+ setData() {
86
+ return new Data({ count: 0 });
87
+ }
88
+
89
+ increment() {
90
+ this.data.count++;
91
+ }
92
+
93
+ render() {
94
+ return Div([
95
+ Button({ click: () => this.increment() }, '+'),
96
+ Span([On('count', (count) => count)])
97
+ ]);
98
+ }
99
+ }
100
+ ```
101
+
102
+ ### Use Jot for:
103
+ - Components with external two-way binding
104
+ - Reusable inputs/controls with value/change pattern
105
+ - Searchable dropdowns, toggles, custom inputs
106
+
107
+ ```javascript
108
+ const Toggle = Jot((checked, setChecked) => (
109
+ Button({
110
+ click: () => setChecked(!checked),
111
+ class: checked ? 'bg-primary' : 'bg-muted'
112
+ }, checked ? 'ON' : 'OFF')
113
+ ));
114
+ ```
115
+
116
+ ## Data Management Patterns
117
+
118
+ ### Reactive Data (setData)
119
+ Use for dynamic values that need reactivity:
120
+
121
+ ```javascript
122
+ setData() {
123
+ return new Data({
124
+ // Simple values
125
+ name: '',
126
+ count: 0,
127
+
128
+ // Arrays (use for dynamic lists)
129
+ items: [],
130
+ selectedIds: [],
131
+
132
+ // Objects
133
+ user: { name: '', email: '' }
134
+ });
135
+ }
136
+
137
+ // Updating data triggers re-render
138
+ this.data.count = 5;
139
+ this.data.items.push(newItem);
140
+ this.data.user.name = 'John';
141
+ ```
142
+
143
+ ### Component States (setupStates)
144
+ Use for discrete state values (modes, flags):
145
+
146
+ ```javascript
147
+ setupStates() {
148
+ return {
149
+ isOpen: false, // Boolean flags
150
+ view: 'list', // String modes: 'list' | 'grid' | 'table'
151
+ tab: 'overview' // Tab selection
152
+ };
153
+ }
154
+
155
+ // Toggle state
156
+ this.state.isOpen = !this.state.isOpen;
157
+ ```
44
158
 
45
- CRITICAL: When children is an array, pass it as the SECOND argument after props, NOT inside props.
159
+ ### Two-Way Binding
160
+ ```javascript
161
+ // Simple binding to data
162
+ Input({ bind: 'username' })
163
+ Input({ bind: 'user.email' })
164
+
165
+ // Binding to external state
166
+ const state = new Data({ value: '' });
167
+ Input({ bind: [state, 'value'] })
168
+
169
+ // Binding with select
170
+ Select({ bind: 'country', options: countries })
171
+
172
+ // Binding with checkbox
173
+ Input({ bind: 'accepted', type: 'checkbox' })
174
+ ```
175
+
176
+ ### Dynamic Lists
177
+ ```javascript
178
+ // map prop - for static/external arrays
179
+ Ul({
180
+ map: [items, (item) => Li(item.name)]
181
+ })
182
+
183
+ // for prop - for component data (reactive)
184
+ Div({
185
+ for: ['items', (item, index) => (
186
+ ItemCard({ item, index })
187
+ )]
188
+ })
189
+
190
+ // NEVER use regular JavaScript .map() for reactive lists
191
+ // ❌ WRONG: Ul([items.map(item => Li(item))])
192
+ // ✅ CORRECT: Ul({ map: [items, (item) => Li(item)] })
193
+ ```
194
+
195
+ ## Watchers and Subscriptions
196
+
197
+ ### On - Watch Data Changes
198
+ ```javascript
199
+ render() {
200
+ return Div([
201
+ // Watch single property
202
+ On('count', (value) => {
203
+ console.log('Count changed:', value);
204
+ }),
205
+
206
+ // Watch nested property
207
+ On('user.name', (name) => {
208
+ this.updateProfile(name);
209
+ }),
210
+
211
+ // Display watched value
212
+ Span({ onState: ['count', (count) => `Count: ${count}`] })
213
+ ]);
214
+ }
215
+ ```
216
+
217
+ ### OnState - Watch State Changes
218
+ ```javascript
219
+ render() {
220
+ return Div([
221
+ OnState('isOpen', (isOpen) => {
222
+ if (isOpen) {
223
+ this.loadContent();
224
+ }
225
+ })
226
+ ]);
227
+ }
228
+ ```
229
+
230
+ ### OnStateOpen - Run Once When State Becomes True
231
+ ```javascript
232
+ render() {
233
+ return Div([
234
+ OnStateOpen('isVisible', () => {
235
+ this.startAnimation();
236
+ })
237
+ ]);
238
+ }
239
+ ```
240
+
241
+ ### UseParent - Access Parent Component
242
+ ```javascript
243
+ const ChildAtom = Atom((props) => (
244
+ Div([
245
+ UseParent(({ data, state, panel }) => {
246
+ // Access parent's data/state
247
+ panel.selectItem(props.id);
248
+ return null;
249
+ })
250
+ ])
251
+ ));
252
+ ```
46
253
 
47
254
  ## File layout to know
48
255
  - `src/components/atoms/**`: Base-level atoms and atom variants (e.g., buttons, icons, badges, tooltips, skeleton, veil).
@@ -52,156 +259,115 @@ CRITICAL: When children is an array, pass it as the SECOND argument after props,
52
259
  - `src/utils/**`: Utilities (formatting, image-scaler with pointer/zoom/drag helpers).
53
260
  - `src/ui.js`: Re-exports public surface used by `vite` lib entries.
54
261
 
55
- ## Working with Icons (CRITICAL)
262
+ ## Tailwind and theming
263
+ - Tailwind scans `./src/ui.js` and `./src/**/*.{js,ts,jsx,tsx}`. If you add files, keep them under `src` and referenced by exports for purge to include classes.
264
+ - Use semantic tokens configured in `tailwind.config.js`: `primary`, `secondary`, `destructive`, `warning`, `muted`, `accent`, `popover`, `card`, `border`, `foreground`, with `DEFAULT` and `foreground` pairs.
265
+ - Dark mode is `media`. Prefer classes already used (`data-[state=active]:...`, rounded tokens via `--radius`).
56
266
 
57
- Icons in this library come from Heroicons and are defined in `src/components/icons/icons.js`. They are stored as SVG strings in a nested object structure.
267
+ ## Working with Icons (READ THIS - Most Common Mistake Area)
58
268
 
59
- ### Icon Structure
60
- - Icons are organized hierarchically: `Icons.home`, `Icons.chat.default`, `Icons.chat.text`, `Icons.arrows.left`, etc.
61
- - Each icon is a raw SVG string with Heroicon styling
269
+ ### Icon Basics
270
+ Icons come from [src/components/icons/icons.js](../src/components/icons/icons.js) (Heroicons library). They're SVG strings organized hierarchically. See [base.wiki/02-Icons.md](../base.wiki/02-Icons.md) for complete guide.
62
271
 
63
- ### How to Use Icons (3 methods)
272
+ ### Three Ways to Use Icons
64
273
 
65
- #### Method 1: Using the Icon atom (RECOMMENDED)
274
+ **Method 1: Icon atom (RECOMMENDED)**
66
275
  ```javascript
67
276
  import { Icon } from '@base-framework/ui/atoms';
68
277
  import { Icons } from '@base-framework/ui/icons';
69
278
 
70
- // Pass icon SVG string as child
71
- Icon({ size: 'sm', class: 'text-blue-500' }, Icons.home)
72
- Icon({ size: 'md' }, Icons.chat.default)
73
- Icon({ size: 'lg' }, Icons.arrows.left)
74
-
75
- // Sizes: xs, sm, md, lg, xl, 2xl, 3xl (default: sm)
279
+ Icon({ size: 'sm' }, Icons.home) // SVG string as CHILD, not in props
280
+ Icon({ size: 'md', class: 'text-blue-500' }, Icons.chat.default)
76
281
  ```
77
282
 
78
- #### Method 2: Using raw I element
283
+ **Method 2: Raw I element**
79
284
  ```javascript
80
285
  import { I } from '@base-framework/atoms';
81
286
  import { Icons } from '@base-framework/ui/icons';
82
287
 
83
- // Use html prop to inject SVG
84
- I({ html: Icons.home, class: 'w-6 h-6' })
85
- I({ html: Icons.chat.dots })
288
+ I({ html: Icons.home, class: 'w-6 h-6' }) // Use html prop
86
289
  ```
87
290
 
88
- #### Method 3: In Button with icon prop
291
+ **Method 3: In Button**
89
292
  ```javascript
90
293
  import { Button } from '@base-framework/ui/atoms';
91
294
  import { Icons } from '@base-framework/ui/icons';
92
295
 
93
- // Icon appears on left by default
94
- Button({ variant: 'withIcon', icon: Icons.plus }, 'Add Item')
95
-
96
- // Icon on right
296
+ Button({ variant: 'withIcon', icon: Icons.plus }, 'Add')
97
297
  Button({ variant: 'withIcon', icon: Icons.arrows.right, position: 'right' }, 'Next')
98
298
  ```
99
299
 
100
- ### Common Icon Access Patterns
300
+ ### Common Icon Paths
101
301
  ```javascript
102
- // Simple icons (top-level)
103
- Icons.home, Icons.star, Icons.help, Icons.plus
104
-
105
- // Nested icons (use dot notation)
106
- Icons.chat.default, Icons.chat.text, Icons.chat.dots
107
- Icons.arrows.left, Icons.arrows.right, Icons.arrows.upDown
108
- Icons.adjustments.vertical, Icons.adjustments.horizontal
109
-
110
- // Icons with states
111
- Icons.locked, Icons.unlocked
112
- Icons.play, Icons.stop, Icons.playing
302
+ // Simple: Icons.home, Icons.star, Icons.help, Icons.plus
303
+ // Nested: Icons.chat.default, Icons.arrows.left, Icons.adjustments.vertical
113
304
  ```
114
305
 
115
- ### CRITICAL MISTAKES TO AVOID
116
- **WRONG**: `Icon(Icons.home)` - Missing props object
117
- **WRONG**: `Icon({ icon: Icons.home })` - Don't use `icon` prop
118
- **WRONG**: `I(Icons.home)` - Must use `html` prop
119
- **WRONG**: `Icons['home']` - Use dot notation, not bracket
120
- ❌ **WRONG**: `Icons.chat` - Incomplete path for nested icons
306
+ ### CRITICAL Icon Mistakes
307
+ ❌ `Icon(Icons.home)` - Missing props object
308
+ ❌ `Icon({ icon: Icons.home })` - Wrong prop name, pass as child
309
+ ❌ `I(Icons.home)` - Must use html prop
310
+ ❌ `Icons['home']` - Use dot notation
121
311
 
122
- **CORRECT**: `Icon({ size: 'sm' }, Icons.home)`
123
- **CORRECT**: `I({ html: Icons.home })`
124
- **CORRECT**: `Icons.chat.default` (for nested icons)
125
-
126
- ## Tailwind and theming
127
- - Tailwind scans `./src/ui.js` and `./src/**/*.{js,ts,jsx,tsx}`. If you add files, keep them under `src` and referenced by exports for purge to include classes.
128
- - Use semantic tokens configured in `tailwind.config.js`: `primary`, `secondary`, `destructive`, `warning`, `muted`, `accent`, `popover`, `card`, `border`, `foreground`, with `DEFAULT` and `foreground` pairs.
129
- - Dark mode is `media`. Prefer classes already used (`data-[state=active]:...`, rounded tokens via `--radius`).
312
+ ✅ `Icon({ size: 'sm' }, Icons.home)`
313
+ ✅ `I({ html: Icons.home })`
314
+ `Button({ icon: Icons.plus }, 'Text')`
130
315
 
131
316
  ## Patterns by example
132
317
 
133
- ### Functional Atom (from `molecules/alert.js`)
134
- Compose small atoms: `Div` containers + `I` for icons + `H5/P` for text. Type variants use lookup table → apply Tailwind classes from map.
135
-
318
+ ### Alert Component (Functional Atom)
136
319
  ```javascript
137
320
  import { Div, H5, I, P } from '@base-framework/atoms';
138
321
  import { Atom } from '@base-framework/base';
139
322
 
140
323
  const AlertIcon = (icon, iconColor) => (
141
- Div({ class: `flex items-center justify-center h-6 w-6 mr-3 ${iconColor}` }, [
142
- I({ html: icon })
324
+ Div({ class: `flex h-6 w-6 mr-3 ${iconColor}` }, [
325
+ I({ html: icon }) // Icon as SVG string
143
326
  ])
144
327
  );
145
328
 
146
329
  export const Alert = Atom(({ title, description, icon, type = 'default' }) => {
147
- const { borderColor, bgColor, iconColor } = typeStyles[type];
148
- return Div({ class: `flex items-start p-4 border rounded-lg ${bgColor} ${borderColor}` }, [
149
- icon && AlertIcon(icon, iconColor),
330
+ const styles = typeStyles[type];
331
+ return Div({ class: `flex p-4 border rounded-lg ${styles.bgColor}` }, [
332
+ icon && AlertIcon(icon, styles.iconColor),
150
333
  Div({ class: 'flex flex-col' }, [
151
334
  H5({ class: 'font-semibold' }, title),
152
- P({ class: 'text-sm text-muted-foreground' }, description)
335
+ P({ class: 'text-sm' }, description)
153
336
  ])
154
337
  ]);
155
338
  });
156
339
  ```
157
340
 
158
- ### Variant pattern (from `atoms/buttons/buttons.js`)
159
- Define variant factories, then export a single `Button` Atom that dispatches by `props.variant`. Icon handling via a shared `IconButton` Atom; support `position: 'right'`.
160
-
341
+ ### Button with Icon (Variant Pattern)
161
342
  ```javascript
162
343
  import { Button as BaseButton } from '@base-framework/atoms';
163
344
  import { Atom } from '@base-framework/base';
164
345
  import { Icon } from '../icon.js';
165
346
 
166
347
  const IconButton = Atom((props, children) => (
167
- BaseButton({
168
- ...props,
169
- class: props.class
170
- }, [
348
+ BaseButton({ ...props }, [
171
349
  props.icon && props.position !== 'right' ? Icon({ size: 'sm' }, props.icon) : null,
172
350
  ...(children || []),
173
351
  props.icon && props.position === 'right' ? Icon({ size: 'sm' }, props.icon) : null
174
352
  ])
175
353
  ));
176
354
 
177
- const BUTTON_VARIANTS = {
178
- primary: DefaultVariant({ class: 'primary' }),
179
- withIcon: WithIconVariant({ class: 'with-icon' })
180
- };
181
-
182
355
  export const Button = Atom((props, children) => {
183
356
  const VariantButton = BUTTON_VARIANTS[props.variant] || BUTTON_VARIANTS.primary;
184
357
  return VariantButton(props, children);
185
358
  });
186
359
  ```
187
360
 
188
- ### Data-driven lists (from `molecules/dropdowns/dropdown.js`)
189
- Use `for: ['collectionKey', (item) => ...]` to render nested collections from component state.
190
-
361
+ ### Data-Driven Lists
191
362
  ```javascript
192
- export const Dropdown = (onSelect) => (
193
- Div({ class: 'w-full z-10' }, [
194
- Div({
195
- class: 'max-h-60 border rounded-md',
196
- for: ['groups', (group) => Group(group, onSelect)]
197
- })
198
- ])
199
- );
200
- ```
363
+ // Using map prop
364
+ Ul({ map: [items, (item) => Li(item.name)] })
201
365
 
202
- ### Stateful Component (from `organisms/lists/data-table.js`)
203
- Use `Component` with `Data` for state management and `declareProps()` for type hints.
366
+ // Using for with state
367
+ Div({ for: ['groups', (group) => Group(group)] })
368
+ ```
204
369
 
370
+ ### Stateful Component
205
371
  ```javascript
206
372
  import { Component, Data } from '@base-framework/base';
207
373
 
@@ -214,77 +380,95 @@ export class DataTable extends Component {
214
380
  setData() {
215
381
  return new Data({
216
382
  selectedRows: [],
217
- hasItems: this.rows && this.rows.length > 0
383
+ hasItems: this.rows?.length > 0
218
384
  });
219
385
  }
220
386
 
221
387
  render() {
222
- return Div({ class: 'w-full' }, [
223
- Table([
224
- TableHeader({ headers: this.headers }),
225
- DataTableBody({ rows: this.rows })
226
- ])
388
+ return Table([
389
+ TableHeader({ headers: this.headers }),
390
+ DataTableBody({ rows: this.rows })
227
391
  ]);
228
392
  }
229
393
  }
230
394
  ```
231
395
 
232
- ## Import Patterns (CRITICAL)
396
+ ## Import Patterns
233
397
 
234
- ### External Framework Imports
398
+ ### From External Packages
235
399
  ```javascript
236
- // DOM elements and reactive utilities
237
- import { Div, Button, Input, I, Ul, Li, H5, P } from '@base-framework/atoms';
238
- import { On, OnState, UseParent } from '@base-framework/atoms';
400
+ // DOM elements
401
+ import { Div, Button, Input, I, Ul, Li, H5, P, Table } from '@base-framework/atoms';
239
402
 
240
- // Core framework classes and utilities
403
+ // Reactive utilities
404
+ import { On, OnState, OnStateOpen, UseParent } from '@base-framework/atoms';
405
+
406
+ // Framework core
241
407
  import { Atom, Component, Data, Jot } from '@base-framework/base';
408
+
409
+ // Routing
410
+ import { router, NavLink } from '@base-framework/base';
242
411
  ```
243
412
 
244
- ### Internal Library Imports
413
+ ### From This Library
245
414
  ```javascript
246
- // Icons (most common mistake area)
415
+ // Icons (ALWAYS import both)
247
416
  import { Icons } from '@base-framework/ui/icons';
248
417
  import { Icon } from '@base-framework/ui/atoms';
249
418
 
250
- // Components from this library
251
- import { Button, Alert } from '@base-framework/ui/atoms';
252
- import { Form, Dropdown } from '@base-framework/ui/molecules';
253
- import { DataTable } from '@base-framework/ui/organisms';
419
+ // Atoms
420
+ import { Button, Badge, Alert } from '@base-framework/ui/atoms';
421
+
422
+ // Molecules
423
+ import { Form, Dropdown, Modal, DatePicker } from '@base-framework/ui/molecules';
424
+
425
+ // Organisms
426
+ import { DataTable, Calendar, TabGroup } from '@base-framework/ui/organisms';
427
+
428
+ // Pages
429
+ import { Page, BasicPage, SidebarMenuPage } from '@base-framework/ui/pages';
430
+
431
+ // Utils
432
+ import { Format, DateTime, ImageScaler } from '@base-framework/ui/utils';
254
433
  ```
255
434
 
256
- ### Relative Imports (when authoring components IN this library)
435
+ ### Relative (when authoring in this repo)
257
436
  ```javascript
258
- // From within src/components/
259
437
  import { Icons } from '../../icons/icons.js';
260
438
  import { Icon } from '../icon.js';
261
- import { Button as BaseButton } from '@base-framework/atoms';
262
439
  ```
263
440
 
264
441
  ## Coding rules (do/don't)
265
442
 
266
- ### DO:
267
- - Import primitives from `@base-framework/atoms` (Div, Button, I, etc.)
268
- - Import framework tools from `@base-framework/base` (Atom, Component, Data)
269
- - Pass children array as SECOND argument: `Div({ class: 'wrapper' }, [child1, child2])`
270
- - Use `Icons` object from `@base-framework/ui/icons` for all icons
271
- - Use `Icon` atom with SVG string as child: `Icon({ size: 'sm' }, Icons.home)`
272
- - Use `I({ html: iconString })` for raw icon rendering
273
- - Export new components from appropriate barrel files
274
- - Use Tailwind semantic tokens (primary, muted-foreground, etc.)
275
- - Use `map: [array, fn]` or `for: ['key', fn]` for lists
276
- - Use `bind: 'path'` or `bind: [state, 'key']` for two-way binding
277
-
278
- ### DON'T:
279
- - Pass children inside props object: `Div({ children: [...] })`
280
- - Use `icon` prop on Icon atom: `Icon({ icon: Icons.home })`
281
- - Pass icon directly without props: `Icon(Icons.home)`
282
- - ❌ Use React/Vue/JSX syntax
283
- -Mutate DOM directly (use Data bindings instead)
284
- - Use raw hex colors (use Tailwind tokens)
285
- - Import Icons from wrong path
286
- - Create new icon implementations (use existing Icon atom)
287
- - Forget to spread props: always use `{ ...defaultProps, ...props }`
443
+ ### DO:
444
+ - Import DOM elements from `@base-framework/atoms`
445
+ - Import Atom, Component, Data from `@base-framework/base`
446
+ - Pass children as SECOND argument: `Div({ class: 'x' }, [children])`
447
+ - Use Icons object: `import { Icons } from '@base-framework/ui/icons'`
448
+ - Use Icon atom: `Icon({ size: 'sm' }, Icons.home)`
449
+ - Use I element for icons: `I({ html: Icons.home })`
450
+ - Spread props: `{ ...defaultProps, ...props }`
451
+ - Use Tailwind semantic tokens (primary, secondary, destructive, warning, muted, accent)
452
+ - Use `map` or `for` for lists: `Ul({ map: [items, fn] })` or `Div({ for: ['items', fn] })`
453
+ - Use `bind` for two-way binding: `Input({ bind: 'username' })`
454
+ - Use `On` for data watchers: `On('count', (val) => ...)`
455
+ - Use `OnState` for state watchers: `OnState('isOpen', (val) => ...)`
456
+ - Use Data for reactive values: `new Data({ count: 0 })`
457
+ - Use setupStates for discrete states: `setupStates() { return { isOpen: false } }`
458
+ - Read documentation in `base.wiki/` for detailed patterns
459
+
460
+ ###DON'T:
461
+ - Pass children in props: `Div({ children: [...] })`
462
+ - Use icon prop on Icon: `Icon({ icon: Icons.home })`
463
+ - Pass icon without props: `Icon(Icons.home)`
464
+ - Use React/Vue/JSX patterns
465
+ - Mutate DOM directly
466
+ - Use raw hex colors (use Tailwind tokens)
467
+ - Import Icons from wrong path
468
+ - Use regular JS map for reactive lists: `[items.map(...)]`
469
+ - Use value + onChange: use `bind` instead
470
+ - Use plain objects for reactive data: use `Data` instead
471
+ - Use useState hooks: use `Data` and `setupStates` instead
288
472
 
289
473
  ## Adding a new component (checklist)
290
474
  1) Decide Atom vs Component (stateless vs stateful/interactive)
@@ -293,131 +477,112 @@ import { Button as BaseButton } from '@base-framework/atoms';
293
477
  4) If it needs data/state, use `Data`/`Jot`, `On`, `bind`, `for` as seen in existing components
294
478
  5) Run dev server and verify render; run build to ensure types emit
295
479
 
296
- ## Common Mistakes & Troubleshooting
297
-
298
- ### Issue: Icons not rendering
299
- **Symptoms**: Icons appear as blank or broken
300
- **Causes**:
301
- 1. Wrong import path for Icons
302
- 2. Incorrect Icon usage syntax
303
- 3. Missing `html` prop on I element
480
+ ## Common Mistakes & Quick Fixes
304
481
 
305
- **Solutions**:
482
+ ### Icons Not Rendering
306
483
  ```javascript
307
- // Correct ways
308
- import { Icons } from '@base-framework/ui/icons';
484
+ // Wrong
485
+ Icon(Icons.home) // Missing props object
486
+ Icon({ icon: Icons.home }) // Wrong prop name, pass as child
487
+ I(Icons.home) // Must use html prop
488
+ Button({ icon: Icons.home }) // Missing variant: 'withIcon'
489
+
490
+ // ✅ Correct
309
491
  Icon({ size: 'sm' }, Icons.home)
310
492
  I({ html: Icons.home })
311
-
312
- // ❌ Wrong ways
313
- Icon(Icons.home) // Missing props object
314
- Icon({ icon: Icons.home }) // Wrong prop name
315
- I(Icons.home) // Missing html prop
493
+ Button({ variant: 'withIcon', icon: Icons.plus }, 'Text')
316
494
  ```
317
495
 
318
- ### Issue: Children not rendering
319
- **Symptoms**: Child components/text not appearing
320
- **Cause**: Passing children in props object instead of as second argument
321
-
322
- **Solution**:
496
+ ### Children Not Appearing
323
497
  ```javascript
498
+ // ❌ Wrong
499
+ Div({ class: 'wrapper', children: [Div('test')] }) // children in props
500
+ Div({ class: 'wrapper' }, Div('test')) // Single child must be array or string
501
+
324
502
  // ✅ Correct
325
- Div({ class: 'wrapper' }, [
326
- Div('child 1'),
327
- Div('child 2')
328
- ])
503
+ Div({ class: 'wrapper' }, [Div('test')]) // Array of children
504
+ Div({ class: 'wrapper' }, 'text only') // Single text child
505
+ ```
329
506
 
507
+ ### Lists Not Rendering
508
+ ```javascript
330
509
  // ❌ Wrong
331
- Div({
332
- class: 'wrapper',
333
- children: [Div('child')]
334
- })
335
- ```
510
+ Ul([items.map(item => Li(item.name))]) // Regular JS map doesn't track reactivity
511
+ Div(items.map(item => ItemCard(item))) // Missing props object, not reactive
336
512
 
337
- ### Issue: Component not reactive
338
- **Symptoms**: UI doesn't update when data changes
339
- **Cause**: Not using Data or proper bindings
513
+ // Correct
514
+ Ul({ map: [items, (item) => Li(item.name)] }) // Static/external arrays
515
+ Div({ for: ['items', (item) => ItemComponent(item)] }) // Component data (reactive)
516
+ ```
340
517
 
341
- **Solution**:
518
+ ### Not Reactive
342
519
  ```javascript
343
- // Use Data in Component
520
+ // Wrong (plain object)
521
+ this.state = { count: 0 };
522
+ this.state.count++; // Doesn't trigger re-render
523
+
524
+ // ✅ Correct (use Data)
344
525
  setData() {
345
526
  return new Data({ count: 0 });
346
527
  }
347
-
348
- // Use bind for two-way binding
349
- Input({ bind: 'username' })
350
- Input({ bind: [externalState, 'email'] })
528
+ // In method:
529
+ this.data.count++; // Triggers re-render
351
530
  ```
352
531
 
353
- ### Issue: List not rendering
354
- **Symptoms**: Array of items not displaying
355
- **Cause**: Using regular map instead of Base's reactive patterns
356
-
357
- **Solution**:
532
+ ### Binding Issues
358
533
  ```javascript
359
- // Use map prop
360
- Ul({ map: [items, (item) => Li(item.name)] })
361
-
362
- // ✅ Use for with state key
363
- Div({ for: ['items', (item) => ItemComponent(item)] })
534
+ // Wrong
535
+ Input({ value: name, change: (e) => setName(e.target.value) }) // React pattern
536
+ Input({ value: this.data.username }) // One-way only, not reactive
364
537
 
365
- // Wrong - regular JS map
366
- Ul([items.map(item => Li(item.name))])
538
+ // Correct
539
+ Input({ bind: 'username' }) // Two-way binding to component data
540
+ Input({ bind: [state, 'email'] }) // Two-way binding to external state
367
541
  ```
368
542
 
369
- ## Commands reference
370
- - Dev: `npm run dev`
371
- - Build: `npm run build`
372
- - Preview: `npm run preview`
373
-
374
- ## Quick Reference Card
375
-
376
- ### Component Creation
543
+ ### Event Handling
377
544
  ```javascript
378
- // Atom (stateless)
379
- export const MyAtom = Atom((props, children) => (
380
- Div({ class: props.class }, children)
381
- ));
382
-
383
- // Component (stateful)
384
- export class MyComponent extends Component {
385
- declareProps() {
386
- this.items = [];
387
- }
388
-
389
- setData() {
390
- return new Data({ selected: null });
391
- }
545
+ // Wrong
546
+ Button({ onClick: () => this.submit() }) // React pattern
547
+ Button({ onclick: () => this.submit() }) // Wrong casing
392
548
 
393
- render() {
394
- return Div([/* ... */]);
395
- }
396
- }
549
+ // ✅ Correct
550
+ Button({ click: () => this.submit() }) // Use 'click' not 'onClick'
397
551
  ```
398
552
 
399
- ### Icon Usage Quick Reference
553
+ ### State Management
400
554
  ```javascript
401
- // In Button
402
- Button({ variant: 'withIcon', icon: Icons.plus }, 'Add')
403
-
404
- // Standalone
405
- Icon({ size: 'md', class: 'text-primary' }, Icons.home)
555
+ // Wrong
556
+ const [isOpen, setIsOpen] = useState(false); // React hooks
557
+ this.isOpen = false; // Plain property
406
558
 
407
- // Raw SVG
408
- I({ html: Icons.check, class: 'w-5 h-5' })
559
+ // Correct
560
+ setupStates() {
561
+ return { isOpen: false };
562
+ }
563
+ // Toggle:
564
+ this.state.isOpen = !this.state.isOpen;
409
565
  ```
410
566
 
411
- ### Event Handlers
567
+ ### Watchers
412
568
  ```javascript
413
- // Click
414
- Button({ click: (e) => console.log('clicked') }, 'Click')
415
-
416
- // Submit (in Form)
417
- Form({ submit: (e, parent) => handleSubmit(e) }, [...])
569
+ // ❌ Wrong
570
+ useEffect(() => { ... }, [count]); // React pattern
571
+ this.data.count.subscribe(val => ...); // No such method
418
572
 
419
- // State-based callbacks
420
- Button({ onState: ['key', { key: 'value' }] }, 'State Button')
573
+ // Correct
574
+ render() {
575
+ return Div([
576
+ On('count', (value) => {
577
+ console.log('Count changed:', value);
578
+ })
579
+ ]);
580
+ }
421
581
  ```
422
582
 
583
+ ## Commands reference
584
+ - Dev: `npm run dev`
585
+ - Build: `npm run build`
586
+ - Preview: `npm run preview`
587
+
423
588
  If anything seems unclear (e.g., preferred binding patterns or where to export), ask for confirmation before large changes.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@base-framework/ui",
3
- "version": "1.0.2030",
3
+ "version": "1.0.2031",
4
4
  "description": "This is a UI package that adds components and atoms that use Tailwind CSS and a theme based on Shadcn.",
5
5
  "main": "./dist/index.es.js",
6
6
  "scripts": {