@_tc/template-core 0.1.13 → 0.1.15

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 (48) hide show
  1. package/.skills/tc-component-usage-skills/SKILL.md +31 -9
  2. package/.skills/tc-component-usage-skills/reference/component-api.md +63 -42
  3. package/.skills/tc-component-usage-skills/reference/examples.md +34 -24
  4. package/.skills/tc-component-usage-skills/reference/patterns.md +52 -17
  5. package/.skills/tc-component-usage-skills/reference/template-core-frontend.md +104 -0
  6. package/.skills/tc-generator/SKILL.md +11 -3
  7. package/.skills/tc-generator/reference/example.md +1 -1
  8. package/.skills/tc-generator/reference/model-schema.md +4 -2
  9. package/.skills/tc-generator/reference/project-template/config/config.default.js +4 -0
  10. package/.skills/tc-generator/reference/project-template/index.js +1 -1
  11. package/.skills/tc-generator/reference/project-template/model/product/project/default.js +1 -0
  12. package/.skills/tc-generator/reference/project-template/package.json +16 -1
  13. package/.skills/tc-generator/reference/runtime-extensions.md +190 -0
  14. package/README.md +217 -2
  15. package/cjs/app/extend/db.js +20 -1
  16. package/cjs/bundler/utils.js +1 -1
  17. package/esm/app/extend/db.js +138 -3
  18. package/esm/bundler/utils.js +2 -1
  19. package/fe/frontend/apps/dash/Dashboard.js +3 -1
  20. package/fe/frontend/apps/dash/types.d.ts +8 -0
  21. package/fe/frontend/src/components/LanguageSwitch/LanguageSwitch.d.ts +14 -0
  22. package/fe/frontend/src/components/LanguageSwitch/LanguageSwitch.js +32 -0
  23. package/fe/frontend/src/components/LanguageSwitch/index.d.ts +4 -0
  24. package/fe/frontend/src/components/LanguageSwitch/index.js +3 -0
  25. package/fe/frontend/src/components/ThemeSwitch/ThemeSwitch.d.ts +15 -0
  26. package/fe/frontend/src/components/ThemeSwitch/ThemeSwitch.js +59 -0
  27. package/fe/frontend/src/components/ThemeSwitch/index.d.ts +4 -0
  28. package/fe/frontend/src/components/ThemeSwitch/index.js +3 -0
  29. package/fe/frontend/src/components/index.d.ts +11 -0
  30. package/fe/frontend/src/components/index.js +10 -0
  31. package/fe/frontend/src/index.d.ts +1 -0
  32. package/fe/frontend/src/index.js +1 -0
  33. package/fe/frontend/src/language/en-US.d.ts +13 -0
  34. package/fe/frontend/src/language/en-US.js +13 -0
  35. package/fe/frontend/src/language/index.d.ts +7 -0
  36. package/fe/frontend/src/language/index.js +7 -0
  37. package/fe/frontend/src/language/zh-CN.d.ts +13 -0
  38. package/fe/frontend/src/language/zh-CN.js +13 -0
  39. package/fe/packages/ui/react/components/Button/{SumbitButton.d.ts → SubmitButton.d.ts} +5 -5
  40. package/fe/packages/ui/react/components/Button/{SumbitButton.js → SubmitButton.js} +3 -3
  41. package/fe/packages/ui/react/components/Button/index.d.ts +1 -1
  42. package/fe/packages/ui/react/components/Button/index.js +1 -1
  43. package/fe/packages/ui/react/components/Modal/ModalManager.d.ts +3 -3
  44. package/fe/packages/ui/react/components/TableSearch/index.d.ts +1 -0
  45. package/package.json +1 -1
  46. package/types/app/extend/db.d.ts +54 -1
  47. package/types/config/config.default.d.ts +30 -0
  48. package/types/index.d.ts +1 -0
@@ -1,21 +1,23 @@
1
1
  ---
2
2
  name: tc-component-usage-skills
3
- description: Use when building or modifying React UI in this TemplateCore project. Covers how to choose, compose, and correctly use components from `@tc/ui-react` (the `packages/ui/react/components/` library). Includes patterns for forms, tables, modals, drawers, menus, and all other TC UI components.
3
+ description: Use when building or modifying React UI in TemplateCore projects. Covers how to choose, compose, and correctly use components from `@tc/ui-react` / `@_tc/template-core/fe/rc`, plus TemplateCore frontend helpers from `@_tc/template-core/fe` such as `AsyncSelect`, `LanguageSwitch`, and `ThemeSwitch`. Includes patterns for forms, tables, modals, drawers, menus, Dashboard toolbar UI, and TC visual style.
4
4
  ---
5
5
 
6
6
  # TC Component Usage
7
7
 
8
- Use this skill when writing React components that consume `@tc/ui-react`.
8
+ Use this skill when writing React components that consume `@tc/ui-react`, `@_tc/template-core/fe/rc`, or TemplateCore frontend helpers from `@_tc/template-core/fe`.
9
9
 
10
10
  ## Quick Decision Flow
11
11
 
12
12
  1. **Displaying data?** → `DataTable` or `Card`
13
- 2. **Collecting input?** → `Form` / `SchemaForm` (built on `rc-field-form`) with `Input`, `Select`, `DatePicker`, `Switch`, `Textarea`, `InputNumber`, `Checkbox`, `Radio`, `TreeSelect`, `Upload`
13
+ 2. **Collecting input?** → `Form` / `SchemaForm` (built on `rc-field-form`) with `Input`, `Select`, `DatePicker`, `Switch`, `Textarea`, `InputNumber`, `Checkbox`, `Radio`, `TreeSelect`, `Upload`; TemplateCore model-driven pages can also use framework field type `asyncSelect`
14
14
  3. **Page layout?** → `Layout`
15
- 4. **Overlays?** → `Modal` / `modalManager.confirm()` / `Drawer` / `Popup` / `Tooltip` / `ImagePreview`
15
+ 4. **Overlays?** → `Modal` / `modal.confirm()` / `Drawer` / `Popup` / `Tooltip` / `ImagePreview`
16
16
  5. **Navigation?** → `Menu` / `Breadcrumb` / `Tabs` / `Pagination`
17
17
  6. **Feedback?** → `message.success()` / `Notification` / `Loading` / `Skeleton` / `ConfirmDialog`
18
18
  7. **Search & filter?** → `TableSearch` / `Search`
19
+ 8. **Language/theme toolbar?** → `LanguageSwitch` / `ThemeSwitch` from `@_tc/template-core/fe`
20
+ 9. **Remote select options?** → `AsyncSelect` from `@_tc/template-core/fe`
19
21
 
20
22
  ## Core Patterns
21
23
 
@@ -36,15 +38,15 @@ Every input-like component supports both modes. Check for `value !== undefined`
36
38
  `Form` / `FormItem` / `SchemaForm` wrap `rc-field-form`. `FormItem` auto-clones children to inject `value`/`onChange`. Prefer `SchemaForm` for data-driven forms:
37
39
 
38
40
  ```tsx
39
- const form = useForm<T>();
41
+ const [form] = useForm<T>();
40
42
  <SchemaForm form={form} schemas={[
41
43
  { key: 'name', label: 'Name', type: 'input', required: true },
42
44
  { key: 'status', label: 'Status', type: 'select', fieldProps: { options: [...] } },
43
45
  ]} onFinish={(vals) => ...} />
44
46
  ```
45
47
 
46
- Built-in `type` values: `input`, `inputNumber`, `select`, `textarea`, `switch`, `date`, `checkbox`.
47
- Custom types are registered via `@tc/scalability/SchemaForm/data`.
48
+ UI-library built-in `type` values: `input`, `inputNumber`, `select`, `textarea`, `switch`, `date`, `checkbox`.
49
+ TemplateCore adds framework type `asyncSelect` via `@tc/scalability/SchemaForm/frameData`; custom app types are registered via `@tc/scalability/SchemaForm/data`.
48
50
 
49
51
  ### Modal (Declarative + Imperative)
50
52
 
@@ -55,7 +57,7 @@ Custom types are registered via `@tc/scalability/SchemaForm/data`.
55
57
  </Modal>
56
58
 
57
59
  // Imperative (async ok supported)
58
- modalManager.confirm({
60
+ modal.confirm({
59
61
  title: 'Confirm',
60
62
  content: 'Are you sure?',
61
63
  onOk: async () => { await doSomething(); },
@@ -94,6 +96,7 @@ Read the reference files (progressive disclosure):
94
96
  - `reference/component-api.md` — Full props tables for every component
95
97
  - `reference/patterns.md` — Deeper patterns: schema forms, table columns, action buttons, horizontal forms
96
98
  - `reference/examples.md` — Copy-paste examples for common scenarios
99
+ - `reference/template-core-frontend.md` — TemplateCore frontend helper components and Dashboard toolbar composition
97
100
 
98
101
  ## Validation
99
102
 
@@ -106,5 +109,24 @@ pnpm type-check
106
109
  ## Component Import Convention
107
110
 
108
111
  ```tsx
109
- import { Button, Form, FormItem, Input, DataTable, Modal } from '@tc/ui/react'
112
+ import { Button, Form, FormItem, Input, DataTable, Modal, modal, useForm } from '@tc/ui-react'
113
+ import { AsyncSelect, LanguageSwitch, ThemeSwitch } from '@_tc/template-core/fe'
110
114
  ```
115
+
116
+ When consuming the published TemplateCore package, `@_tc/template-core/fe/rc` re-exports the UI component library:
117
+
118
+ ```tsx
119
+ import { Button, DataTable, TableSearch } from '@_tc/template-core/fe/rc'
120
+ ```
121
+
122
+ Style entry:
123
+
124
+ ```tsx
125
+ import '@tc/ui-react/index.css'
126
+ // or, in published TemplateCore frontend entries:
127
+ import '@_tc/template-core/fe/tailwind_ui.css'
128
+ ```
129
+
130
+ ## Visual Style
131
+
132
+ For `packages/ui/react/components` or TemplateCore business UI, keep the default product feel dark, restrained, dense, and tool-like: stable small type, subtle borders, consistent focus rings, controlled saturated accents, and complete default/hover/focus/active/disabled/loading/error states.
@@ -1,6 +1,6 @@
1
1
  # Component API Reference
2
2
 
3
- Complete props for every `@tc/ui/react` component.
3
+ Complete props for every `@tc/ui-react` component.
4
4
 
5
5
  ---
6
6
 
@@ -24,6 +24,16 @@ interface ButtonProps {
24
24
 
25
25
  `variant='link'` renders `<a>` tag. `SubmitButton` / `AsynchronousButton` auto-manage loading state on async onClick.
26
26
 
27
+ ### SubmitButton / AsynchronousButton
28
+
29
+ ```typescript
30
+ interface SubmitButtonProps extends Omit<ButtonElementProps, 'onClick'> {
31
+ onClick?: (e: MouseEvent<HTMLButtonElement>) => Promise<void | boolean> | void
32
+ }
33
+ ```
34
+
35
+ Use `SubmitButton` or `AsynchronousButton` for async actions that should show loading and ignore repeated clicks while running. The old misspelled `SumbitButton` name should not be used in new code.
36
+
27
37
  ---
28
38
 
29
39
  ## Input
@@ -47,20 +57,21 @@ Note: `onChange` callback receives `(stringValue, event)` — two arguments, not
47
57
 
48
58
  ```typescript
49
59
  interface InputNumberProps {
50
- value?: number | null
60
+ value?: number
51
61
  defaultValue?: number
52
- onChange?: (value: number | null) => void
62
+ onChange?: (value: number | undefined, event: ChangeEvent<HTMLInputElement>) => void
53
63
  min?: number
54
64
  max?: number
55
65
  precision?: number // decimal places
56
66
  step?: number // default: 1
67
+ allowClear?: boolean | { clearIcon: ReactNode }
57
68
  allowStep?: boolean // show stepper buttons
58
69
  disabled?: boolean
59
70
  className?: string
60
71
  }
61
72
  ```
62
73
 
63
- Invalid/empty input becomes `null`. Raw during focus, formatted on blur.
74
+ Invalid/empty input becomes `undefined`. Raw during focus, formatted on blur.
64
75
 
65
76
  ---
66
77
 
@@ -145,23 +156,22 @@ interface SwitchProps {
145
156
 
146
157
  ```typescript
147
158
  interface RadioGroupProps {
148
- value?: string | number
149
- defaultValue?: string | number
150
- onChange?: (value: string | number) => void
159
+ value?: string
160
+ defaultValue?: string
161
+ onChange?: (value: string) => void
151
162
  direction?: 'horizontal' | 'vertical'
152
163
  className?: string
153
164
  children?: ReactNode
154
165
  }
155
166
 
156
- interface RadioProps {
157
- value: string | number
167
+ interface RadioProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'children'> {
168
+ label?: ReactNode
158
169
  disabled?: boolean
159
170
  className?: string
160
- children?: ReactNode
161
171
  }
162
172
  ```
163
173
 
164
- Compound components: `<Radio.Group>` + `<Radio>`. Radio auto-detects parent context.
174
+ Use `<RadioGroup>` with `<Radio value="..." label="..." />`. Radio auto-detects parent context; group selection only handles string values.
165
175
 
166
176
  ---
167
177
 
@@ -304,7 +314,7 @@ interface SchemaFormProps<T> {
304
314
  }
305
315
  ```
306
316
 
307
- **Important:** In `horizontal` layout, `footerButtons` are NOT auto-rendered. Built-in types: `input`, `inputNumber`, `select`, `textarea`, `switch`, `date`, `checkbox`. Extensible via `@tc/scalability/SchemaForm/data`.
317
+ **Important:** In `horizontal` layout, `footerButtons` are NOT auto-rendered. UI-library built-in types: `input`, `inputNumber`, `select`, `textarea`, `switch`, `date`, `checkbox`. TemplateCore also registers framework type `asyncSelect`. App types are extensible via `@tc/scalability/SchemaForm/data`.
308
318
 
309
319
  ---
310
320
 
@@ -324,6 +334,18 @@ interface DataTableProps<T> {
324
334
 
325
335
  Internal pagination (pageSize: 10) when no external pagination prop. Action columns are auto-sticky right. Loading shows skeleton overlay. Empty state shows no-result image.
326
336
 
337
+ ### RowSelection
338
+
339
+ ```typescript
340
+ interface RowSelection<T> {
341
+ selectedRowKeys?: Array<string | number>
342
+ defaultSelectedRowKeys?: Array<string | number>
343
+ onChange?: (selectedRowKeys: Array<string | number>, selectedRows: T[], isAll: boolean) => void
344
+ isAll?: boolean
345
+ beforeCheckboxNode?: ReactNode
346
+ }
347
+ ```
348
+
327
349
  ### TableColumnDef
328
350
 
329
351
  ```typescript
@@ -335,17 +357,16 @@ Internal pagination (pageSize: 10) when no external pagination prop. Action colu
335
357
  key: string
336
358
  title: string
337
359
  dataIndex: string
338
- width?: number
339
- minWidth?: number
340
- maxWidth?: number
360
+ width?: string
341
361
  fixed?: 'left' | 'right'
342
- sortable?: boolean
343
- filterable?: boolean
362
+ isAction?: boolean
363
+ actions?: DropdownItem[] | ((value, record, index) => DropdownItem[])
364
+ actionOverflow?: 'auto' | 'dropdown' | 'inline'
344
365
  render?: (value, record, index) => ReactNode
345
366
  }
346
367
  ```
347
368
 
348
- Action columns render via `ActionBtn` component which supports `'auto'` | `'dropdown'` | `'inline'` overflow modes.
369
+ Action columns are detected by `key: 'action'` or `isAction: true`, and render via `ActionBtn` with `'auto'` | `'dropdown'` | `'inline'` overflow modes.
349
370
 
350
371
  ---
351
372
 
@@ -393,13 +414,13 @@ interface ModalProps {
393
414
  }
394
415
  ```
395
416
 
396
- ### ModalManager (imperative)
417
+ ### Modal Function API (imperative)
397
418
 
398
419
  ```typescript
399
- modalManager.open(config: ModalConfig): string // returns id
400
- modalManager.confirm(config: ConfirmConfig): void // async onOk supported
401
- modalManager.close(id: string): void
402
- modalManager.closeAll(): void
420
+ modal.open(config: ModalConfig): string // returns id
421
+ modal.confirm(config: ConfirmConfig): string // async onOk supported
422
+ modal.close(id: string): void
423
+ modal.closeAll(): void
403
424
  ```
404
425
 
405
426
  ---
@@ -457,23 +478,22 @@ Wraps `Modal` with width: 450 and auto async-supporting ok button.
457
478
 
458
479
  ```typescript
459
480
  interface PopupProps {
460
- content: ReactNode
461
- children: ReactElement // single child, acts as trigger
462
- placement?: Placement
463
- visible?: boolean // controlled
464
- defaultVisible?: boolean
465
- onVisibleChange?: (visible: boolean) => void
466
- trigger?: 'hover' | 'click'
467
- mouseEnterDelay?: number
468
- mouseLeaveDelay?: number
481
+ open: boolean
482
+ anchorRef: RefObject<HTMLElement | null>
483
+ children: ReactNode // popup panel content
484
+ placement?: DropdownPlacement // default: 'bottom-end'
485
+ offset?: number // default: 8
469
486
  keepMounted?: boolean
470
- hoverBridge?: boolean
487
+ closeOnClickOutside?: boolean
488
+ closeOnEscape?: boolean
489
+ matchAnchorWidth?: boolean
490
+ onOpenChange?: (open: boolean) => void
471
491
  className?: string
472
- getContainer?: HTMLElement | (() => HTMLElement)
492
+ container?: HTMLElement
473
493
  }
474
494
  ```
475
495
 
476
- Auto-positioned. `hoverBridge` provides safe mouse movement gap.
496
+ Low-level positioned popup. For click-triggered menus, prefer `Dropdown`; for hints, prefer `Tooltip`.
477
497
 
478
498
  ---
479
499
 
@@ -487,14 +507,15 @@ interface DropdownItem {
487
507
  danger?: boolean
488
508
  onClick?: () => void
489
509
  // Or custom render:
490
- render?: () => ReactNode
510
+ render?: (item, context: { onClick: () => void; close: () => void }) => ReactNode
491
511
  }
492
512
 
493
513
  interface DropdownProps {
494
514
  items: DropdownItem[]
495
- children: ReactElement
496
- placement?: Placement
497
- // ... other Popup props
515
+ children: ReactNode
516
+ placement?: DropdownPlacement
517
+ className?: string
518
+ getPopupContainer?: () => HTMLElement
498
519
  }
499
520
  ```
500
521
 
@@ -506,9 +527,9 @@ interface DropdownProps {
506
527
  interface TooltipProps {
507
528
  content: ReactNode
508
529
  children: ReactElement
509
- placement?: Placement
530
+ placement?: 'top' | 'bottom' | 'left' | 'right'
510
531
  visible?: boolean
511
- // ... other Popup-like props
532
+ className?: string
512
533
  }
513
534
  ```
514
535
 
@@ -618,7 +639,7 @@ interface PaginationProps {
618
639
  current: number
619
640
  pageSize: number
620
641
  total: number
621
- onChange: (page: number, pageSize: number) => void
642
+ onChange: (page: number) => void
622
643
  onPageSizeChange?: (pageSize: number) => void
623
644
  pageSizeOptions?: number[] // default: [10, 20, 50, 100]
624
645
  className?: string
@@ -8,8 +8,8 @@ Copy-paste starting points for common TC UI scenarios.
8
8
 
9
9
  ```tsx
10
10
  import { useState, useEffect, useCallback } from 'react'
11
- import { DataTable, TableSearch, Button, Modal, SchemaForm, ConfirmDialog, message } from '@tc/ui/react'
12
- import type { TableColumnDef, TableSearchSchema } from '@tc/ui/react'
11
+ import { DataTable, TableSearch, Button, Modal, SchemaForm, ConfirmDialog, message, useForm } from '@tc/ui-react'
12
+ import type { TableColumnDef, TableSearchSchema } from '@tc/ui-react'
13
13
 
14
14
  interface Product {
15
15
  id: number
@@ -36,7 +36,7 @@ export function ProductList() {
36
36
  const [modalOpen, setModalOpen] = useState(false)
37
37
  const [editingId, setEditingId] = useState<number | null>(null)
38
38
  const [deleteId, setDeleteId] = useState<number | null>(null)
39
- const [form] = Form.useForm<ProductForm>()
39
+ const [form] = useForm<ProductForm>()
40
40
 
41
41
  const searchSchemas: TableSearchSchema<ProductSearch>[] = [
42
42
  { key: 'name', label: 'Name', type: 'input', isPermanent: true },
@@ -45,7 +45,7 @@ export function ProductList() {
45
45
  ]
46
46
 
47
47
  const columns: TableColumnDef<Product>[] = [
48
- { key: 'id', title: 'ID', dataIndex: 'id', width: 80 },
48
+ { key: 'id', title: 'ID', dataIndex: 'id', width: '80px' },
49
49
  { key: 'name', title: 'Name', dataIndex: 'name' },
50
50
  { key: 'price', title: 'Price', dataIndex: 'price',
51
51
  render: (value) => `¥${value}` },
@@ -56,10 +56,10 @@ export function ProductList() {
56
56
  </span>
57
57
  ) },
58
58
  { key: 'createdAt', title: 'Created', dataIndex: 'createdAt' },
59
- { key: 'action', title: 'Actions', type: 'action',
60
- actions: [
61
- { key: 'edit', label: 'Edit', onClick: (record) => openEdit(record) },
62
- { key: 'delete', label: 'Delete', danger: true, onClick: (record) => setDeleteId(record.id) },
59
+ { key: 'action', title: 'Actions',
60
+ actions: (_value, record) => [
61
+ { key: 'edit', label: 'Edit', onClick: () => openEdit(record) },
62
+ { key: 'delete', label: 'Delete', danger: true, onClick: () => setDeleteId(record.id) },
63
63
  ] },
64
64
  ]
65
65
 
@@ -164,7 +164,7 @@ export function ProductList() {
164
164
  ## Example 2: Detail Drawer
165
165
 
166
166
  ```tsx
167
- import { Drawer, Button, Loading, Skeleton } from '@tc/ui/react'
167
+ import { Drawer, Button, Loading, Skeleton } from '@tc/ui-react'
168
168
 
169
169
  export function UserDetailDrawer({ userId, open, onClose }: {
170
170
  userId: number | null
@@ -207,7 +207,7 @@ export function UserDetailDrawer({ userId, open, onClose }: {
207
207
  ## Example 3: App Shell with Sidebar Menu
208
208
 
209
209
  ```tsx
210
- import { Layout, Menu, Breadcrumb } from '@tc/ui/react'
210
+ import { Layout, Menu, Breadcrumb } from '@tc/ui-react'
211
211
  import { useNavigate, useLocation } from 'react-router-dom'
212
212
 
213
213
  export function AppShell({ children }: { children: ReactNode }) {
@@ -253,7 +253,7 @@ export function AppShell({ children }: { children: ReactNode }) {
253
253
  ## Example 4: Image Upload Form
254
254
 
255
255
  ```tsx
256
- import { SchemaForm, ImageUpload } from '@tc/ui/react'
256
+ import { SchemaForm, ImageUpload, message, useForm } from '@tc/ui-react'
257
257
 
258
258
  interface BannerForm {
259
259
  title: string
@@ -261,7 +261,7 @@ interface BannerForm {
261
261
  }
262
262
 
263
263
  function BannerEditor() {
264
- const [form] = Form.useForm<BannerForm>()
264
+ const [form] = useForm<BannerForm>()
265
265
 
266
266
  return (
267
267
  <SchemaForm<BannerForm>
@@ -269,7 +269,7 @@ function BannerEditor() {
269
269
  schemas={[
270
270
  { key: 'title', label: 'Title', type: 'input', required: true },
271
271
  { key: 'images', label: 'Banner Images', type: 'input',
272
- render: ({ value, onChange }) => (
272
+ render: (value, onChange) => (
273
273
  <ImageUpload
274
274
  value={value}
275
275
  onChange={onChange}
@@ -295,7 +295,7 @@ function BannerEditor() {
295
295
  ## Example 5: TreeSelect for Permissions
296
296
 
297
297
  ```tsx
298
- import { TreeSelect } from '@tc/ui/react'
298
+ import { TreeSelect } from '@tc/ui-react'
299
299
 
300
300
  function PermissionEditor() {
301
301
  const [checked, setChecked] = useState<(string | number)[]>([])
@@ -326,7 +326,7 @@ function PermissionEditor() {
326
326
  ## Example 6: DataTable with Row Selection (Batch Actions)
327
327
 
328
328
  ```tsx
329
- import { DataTable, Button, message } from '@tc/ui/react'
329
+ import { DataTable, Button, message } from '@tc/ui-react'
330
330
 
331
331
  function BatchUserList() {
332
332
  const [selectedKeys, setSelectedKeys] = useState<(string | number)[]>([])
@@ -368,7 +368,7 @@ function BatchUserList() {
368
368
  ## Example 7: DatePicker Usage
369
369
 
370
370
  ```tsx
371
- import { DatePicker } from '@tc/ui/react'
371
+ import { DatePicker } from '@tc/ui-react'
372
372
 
373
373
  // Single date
374
374
  <DatePicker
@@ -398,7 +398,8 @@ import { DatePicker } from '@tc/ui/react'
398
398
  ## Example 8: Tooltip & Popup
399
399
 
400
400
  ```tsx
401
- import { Tooltip, Popup, Dropdown, Button } from '@tc/ui/react'
401
+ import { Tooltip, Popup, Dropdown, Button } from '@tc/ui-react'
402
+ import { useRef, useState } from 'react'
402
403
 
403
404
  // Simple tooltip
404
405
  <Tooltip content="Click to save changes">
@@ -415,11 +416,20 @@ import { Tooltip, Popup, Dropdown, Button } from '@tc/ui/react'
415
416
  </Dropdown>
416
417
 
417
418
  // Custom popup
418
- <Popup
419
- content={<UserCard user={user} />}
420
- trigger="click"
421
- onVisibleChange={(v) => console.log('popup:', v)}
422
- >
423
- <span className="cursor-pointer underline">{user.name}</span>
424
- </Popup>
419
+ function UserPopup({ user }: { user: User }) {
420
+ const [open, setOpen] = useState(false)
421
+ const anchorRef = useRef<HTMLButtonElement>(null)
422
+
423
+ return (
424
+ <>
425
+ <button ref={anchorRef} type="button" onClick={() => setOpen((v) => !v)}
426
+ className="cursor-pointer underline">
427
+ {user.name}
428
+ </button>
429
+ <Popup open={open} anchorRef={anchorRef} onOpenChange={setOpen}>
430
+ <UserCard user={user} />
431
+ </Popup>
432
+ </>
433
+ )
434
+ }
425
435
  ```
@@ -1,6 +1,20 @@
1
1
  # Component Patterns
2
2
 
3
- Recurring patterns and best practices for `@tc/ui/react`.
3
+ Recurring patterns and best practices for `@tc/ui-react`.
4
+
5
+ ---
6
+
7
+ ## Visual Style Pattern
8
+
9
+ For TemplateCore component work, aim for a dark, restrained, collaboration-tool feel:
10
+
11
+ - Use deep backgrounds, low-contrast panels, subtle borders, and compact text.
12
+ - Reserve saturated color for primary actions, selection, status, and collaboration identity.
13
+ - Keep page shells and panels layered; avoid decorative gradients or oversized marketing-style sections for admin UI.
14
+ - Every reusable component should cover default, hover, focus, active, disabled, loading, and error states where applicable.
15
+ - Prefer stable dimensions for controls, table cells, icon buttons, and fixed-format UI so state changes do not shift layout.
16
+
17
+ When editing this repository, `packages/ui/react/components/VIDEO_UI_STYLE_GUIDE.md` is the fuller visual reference.
4
18
 
5
19
  ---
6
20
 
@@ -35,13 +49,33 @@ const [form] = useForm<UserForm>()
35
49
 
36
50
  ### Registering Custom SchemaForm Types
37
51
 
52
+ TemplateCore already registers framework field type `asyncSelect`:
53
+
38
54
  ```tsx
39
- import { registerComponents } from '@tc/scalability/SchemaForm/data'
55
+ <SchemaForm
56
+ schemas={[
57
+ {
58
+ key: 'ownerId',
59
+ label: 'Owner',
60
+ type: 'asyncSelect',
61
+ fieldProps: {
62
+ fetchConfig: { url: '/user/options', method: 'get' },
63
+ },
64
+ },
65
+ ]}
66
+ />
67
+ ```
68
+
69
+ The options endpoint should return the normal TemplateCore response wrapper, usually via `this.success(ctx, options)`.
40
70
 
41
- registerComponents({
71
+ ```tsx
72
+ // In the app-side module aliased as @tc/scalability/SchemaForm/data:
73
+ const consumerComponentsMap = {
42
74
  richText: RichTextEditor,
43
75
  userSelector: UserSelect,
44
- })
76
+ }
77
+
78
+ export default consumerComponentsMap
45
79
  // Now { type: 'richText', ... } works in schemas
46
80
  ```
47
81
 
@@ -61,16 +95,16 @@ When you need custom logic inside a form field:
61
95
 
62
96
  `FormItem` auto-clones children to inject `value`/`onChange`. It handles Input's two-arg `onChange(value, event)` signature correctly.
63
97
 
64
- ### SubmitButton for Async Forms
98
+ ### SubmitButton / AsynchronousButton for Async Forms
65
99
 
66
100
  ```tsx
67
- <SubmitButton
101
+ <AsynchronousButton
68
102
  onClick={async () => {
69
103
  await submitToApi(form.getFieldsValue())
70
104
  }}
71
105
  >
72
106
  Save
73
- </SubmitButton>
107
+ </AsynchronousButton>
74
108
  ```
75
109
 
76
110
  Auto-shows loading spinner during async execution.
@@ -83,14 +117,14 @@ Auto-shows loading spinner during async execution.
83
117
 
84
118
  ```tsx
85
119
  const columns: TableColumnDef<User>[] = [
86
- { key: 'id', title: 'ID', dataIndex: 'id', width: 80 },
120
+ { key: 'id', title: 'ID', dataIndex: 'id', width: '80px' },
87
121
  { key: 'name', title: 'Name', dataIndex: 'name' },
88
122
  { key: 'status', title: 'Status', dataIndex: 'status',
89
123
  render: (value) => <Badge status={value} /> },
90
- { key: 'action', title: 'Actions', type: 'action',
91
- actions: [
92
- { key: 'edit', label: 'Edit', onClick: (record) => openEdit(record) },
93
- { key: 'delete', label: 'Delete', danger: true, onClick: (record) => onDelete(record) },
124
+ { key: 'action', title: 'Actions',
125
+ actions: (_value, record) => [
126
+ { key: 'edit', label: 'Edit', onClick: () => openEdit(record) },
127
+ { key: 'delete', label: 'Delete', danger: true, onClick: () => onDelete(record) },
94
128
  ] },
95
129
  ]
96
130
  ```
@@ -182,7 +216,7 @@ const [form] = useForm()
182
216
  footer={
183
217
  <div className="flex justify-end gap-2">
184
218
  <Button onClick={() => setOpen(false)}>Cancel</Button>
185
- <SubmitButton onClick={() => form.submit()}>Save</SubmitButton>
219
+ <AsynchronousButton onClick={() => form.submit()}>Save</AsynchronousButton>
186
220
  </div>
187
221
  }
188
222
  >
@@ -203,10 +237,10 @@ const [form] = useForm()
203
237
  />
204
238
  ```
205
239
 
206
- ### Imperative Confirm (modalManager)
240
+ ### Imperative Confirm (modal)
207
241
 
208
242
  ```tsx
209
- modalManager.confirm({
243
+ modal.confirm({
210
244
  title: 'Delete',
211
245
  content: 'Are you sure?',
212
246
  onOk: async () => { await deleteUser(id); },
@@ -277,7 +311,7 @@ In `mode='top'`, SubMenus always use popup dropdowns.
277
311
  ### Toast Messages
278
312
 
279
313
  ```tsx
280
- import { message } from '@tc/ui/react'
314
+ import { message } from '@tc/ui-react'
281
315
 
282
316
  message.success('User created successfully')
283
317
  message.error('Failed to save')
@@ -288,7 +322,7 @@ message.info('New update available')
288
322
  ### Notifications (Corner)
289
323
 
290
324
  ```tsx
291
- import { Notification } from '@tc/ui/react'
325
+ import { Notification } from '@tc/ui-react'
292
326
 
293
327
  // As component
294
328
  <Notification type="error" title="Upload Failed" message="Network error. Retry?" />
@@ -351,6 +385,7 @@ This means both patterns work identically:
351
385
 
352
386
  ## CSS Conventions
353
387
 
388
+ - Import styles before using components: `@tc/ui-react/index.css` for raw UI usage, or `@_tc/template-core/fe/tailwind_ui.css` for published TemplateCore frontend entries.
354
389
  - All CSS classes are prefixed `tc-ui-<component-name>`
355
390
  - Tailwind CSS used for styling
356
391
  - Theme via CSS custom properties: `hsl(var(--theme-primary))`, `hsl(var(--theme-background))`, etc.