hs-uix 1.0.1 → 1.0.3

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/README.md CHANGED
@@ -5,81 +5,191 @@
5
5
 
6
6
  Production-ready UI components for [HubSpot UI Extensions](https://developers.hubspot.com/docs/apps/developer-platform/add-features/ui-extensions/overview). Built entirely on HubSpot's native primitives — no custom HTML, no CSS, no iframes.
7
7
 
8
- ## Components
9
-
10
- | Component | Description | Docs |
11
- |-----------|-------------|------|
12
- | **DataTable** | Filterable, sortable, paginated table with auto-sized columns, inline editing, row grouping, and more | [Full documentation](./packages/datatable/README.md) |
13
- | **FormBuilder** | Declarative, config-driven form with validation, multi-step wizards, and 20+ field types | [Full documentation](./packages/form/README.md) |
14
-
15
8
  ## Install
16
9
 
17
10
  ```bash
18
11
  npm install hs-uix
19
12
  ```
20
13
 
21
- ## Quick Start
14
+ ```jsx
15
+ import { DataTable } from "hs-uix/datatable";
16
+ import { FormBuilder } from "hs-uix/form";
17
+
18
+ // or import everything from the root
19
+ import { DataTable, FormBuilder } from "hs-uix";
20
+ ```
22
21
 
23
- ### DataTable
22
+ Requires `react` >= 18.0.0 and `@hubspot/ui-extensions` >= 0.12.0 as peer dependencies (already present in any HubSpot UI Extensions project).
23
+
24
+ ---
25
+
26
+ # DataTable
27
+
28
+ A drop-in table component for HubSpot UI Extensions. Define your columns, pass your data, and you get search, filtering, sorting, pagination, inline editing, row grouping, and auto-sized columns out of the box.
29
+
30
+ ![Full-Featured DataTable](https://raw.githubusercontent.com/05bmckay/hs-uix/main/packages/datatable/assets/fully-featured-table.png)
31
+
32
+ ## Quick Start
24
33
 
25
34
  ```jsx
26
35
  import { DataTable } from "hs-uix/datatable";
27
36
 
28
- const columns = [
29
- { field: "name", label: "Name", sortable: true },
30
- { field: "status", label: "Status" },
31
- { field: "amount", label: "Amount", sortable: true },
37
+ const COLUMNS = [
38
+ { field: "name", label: "Company", sortable: true, renderCell: (val) => val },
39
+ { field: "status", label: "Status", renderCell: (val) => <StatusTag>{val}</StatusTag> },
40
+ { field: "amount", label: "Amount", sortable: true, renderCell: (val) => formatCurrency(val) },
32
41
  ];
33
42
 
34
- <DataTable data={deals} columns={columns} searchFields={["name"]} pageSize={10} />;
43
+ <DataTable data={deals} columns={COLUMNS} searchFields={["name"]} pageSize={10} />
35
44
  ```
36
45
 
37
- ### FormBuilder
46
+ That's a searchable, sortable, paginated table with auto-sized columns in 5 lines of config.
47
+
48
+ ## Features
49
+
50
+ - Full-text search with optional fuzzy matching via Fuse.js
51
+ - Select, multi-select, and date range filters with active badges and clear/reset controls
52
+ - Click-to-sort headers with three-state cycling
53
+ - Client-side or server-side pagination
54
+ - Collapsible row groups with per-column aggregation
55
+ - Row selection with bulk action bar
56
+ - Per-row actions via `rowActions`
57
+ - Two edit modes (discrete click-to-edit and inline always-visible) supporting 12 input types with validation
58
+ - Auto-width column sizing based on data analysis
59
+ - Column-level footer for totals rows
60
+ - Works with `useAssociations` for live CRM data
61
+ - Server-side mode with loading/error states, search debounce, and unified `onParamsChange` callback
62
+
63
+ ## Highlights
64
+
65
+ ### Filters & Footer Totals
66
+
67
+ ![Active Filters](https://raw.githubusercontent.com/05bmckay/hs-uix/main/packages/datatable/assets/fully-featured-table-active-filters.png)
68
+
69
+ ### Row Selection & Bulk Actions
70
+
71
+ ![Row Selection with Action Bar](https://raw.githubusercontent.com/05bmckay/hs-uix/main/packages/datatable/assets/action-bar-per-row-actions.png)
72
+
73
+ ### Inline Editing
74
+
75
+ ![Discrete Editing](https://raw.githubusercontent.com/05bmckay/hs-uix/main/packages/datatable/assets/inline-editing-discreet.png)
76
+
77
+ Two edit modes: **discrete** (click-to-edit, default) and **inline** (always-visible inputs). Supports `text`, `textarea`, `number`, `currency`, `stepper`, `select`, `multiselect`, `date`, `time`, `datetime`, `toggle`, and `checkbox`.
78
+
79
+ ### Row Grouping
80
+
81
+ ![Row Grouping](https://raw.githubusercontent.com/05bmckay/hs-uix/main/packages/datatable/assets/row-grouping.png)
82
+
83
+ ### Full-Row Editing
84
+
85
+ ![Full-Row Editing](https://raw.githubusercontent.com/05bmckay/hs-uix/main/packages/datatable/assets/full-row-editing.png)
86
+
87
+ ### useAssociations
88
+
89
+ ![useAssociations + DataTable](https://raw.githubusercontent.com/05bmckay/hs-uix/main/packages/datatable/assets/useAssociations.png)
90
+
91
+ Connect live CRM data (contacts, deals, tickets, etc.) to a DataTable with `useAssociations` from `@hubspot/ui-extensions/crm`.
92
+
93
+ > **Full documentation:** [DataTable README](./packages/datatable/README.md) — includes full API reference, all examples, server-side mode, and more.
94
+
95
+ ---
96
+
97
+ # FormBuilder
98
+
99
+ Declarative, config-driven forms for HubSpot UI Extensions. Define fields as data, get a complete form with validation, layout, multi-step wizards, and full HubSpot component integration.
100
+
101
+ ![Basic Form](https://raw.githubusercontent.com/05bmckay/hs-uix/main/packages/form/assets/basic-form.png)
102
+
103
+ ## Quick Start
38
104
 
39
105
  ```jsx
40
106
  import { FormBuilder } from "hs-uix/form";
41
107
 
42
108
  const fields = [
43
- { name: "email", label: "Email", type: "text", required: true },
44
- { name: "role", label: "Role", type: "select", options: [
45
- { label: "Admin", value: "admin" },
46
- { label: "User", value: "user" },
47
- ]},
109
+ { name: "firstName", type: "text", label: "First name", required: true },
110
+ { name: "lastName", type: "text", label: "Last name", required: true },
111
+ { name: "email", type: "text", label: "Email", pattern: /^[^\s@]+@[^\s@]+$/, patternMessage: "Enter a valid email" },
48
112
  ];
49
113
 
50
- <FormBuilder fields={fields} onSubmit={(values) => console.log(values)} />;
114
+ <FormBuilder
115
+ columns={2}
116
+ fields={fields}
117
+ onSubmit={(values) => console.log(values)}
118
+ />
51
119
  ```
52
120
 
53
- You can also import everything from the root:
121
+ ## Features
54
122
 
55
- ```jsx
56
- import { DataTable, FormBuilder } from "hs-uix";
57
- ```
123
+ - 20+ field types mapping to native HubSpot components (`text`, `number`, `select`, `date`, `toggle`, `repeater`, `crmPropertyList`, and more)
124
+ - Four layout modes: fixed columns, responsive AutoGrid, explicit row layout, and legacy half-width
125
+ - Built-in validation chain: required, pattern, length/range, custom sync, and custom async with loading indicators
126
+ - Conditional visibility and dependent property grouping
127
+ - Multi-step wizards with per-step validation
128
+ - Repeater fields for dynamic add/remove rows
129
+ - Accordion sections and field group dividers
130
+ - Custom field type plugin registry
131
+ - Controlled and uncontrolled modes
132
+ - Ref API for imperative access (`submit`, `validate`, `reset`, `getValues`, `setFieldValue`, etc.)
133
+ - Submit lifecycle hooks (`transformValues`, `onBeforeSubmit`, `onSubmitSuccess`, `onSubmitError`)
134
+ - Auto-save, dirty tracking, read-only mode, and form-level alerts
58
135
 
59
- ## Local Development
136
+ ## Highlights
60
137
 
61
- ```bash
62
- # Install dependencies
63
- npm install
138
+ ### Layout
64
139
 
65
- # Build
66
- npm run build
140
+ ![Explicit Layout](https://raw.githubusercontent.com/05bmckay/hs-uix/main/packages/form/assets/explicit-layout-weighted.png)
67
141
 
68
- # Watch mode
69
- npm run dev
70
- ```
142
+ ### Conditional Visibility & Dependent Properties
143
+
144
+ ![Dependent & Cascading](https://raw.githubusercontent.com/05bmckay/hs-uix/main/packages/form/assets/dependent-cascading.gif)
145
+
146
+ ### Async Validation
147
+
148
+ ![Async Validation](https://raw.githubusercontent.com/05bmckay/hs-uix/main/packages/form/assets/async-validation-side-effects.png)
149
+
150
+ ### Repeater Fields
151
+
152
+ ![Repeater Fields](https://raw.githubusercontent.com/05bmckay/hs-uix/main/packages/form/assets/repeater-fields.png)
71
153
 
72
- ## Monorepo Structure
154
+ ### Sections & Groups
73
155
 
156
+ ![Sections & Groups](https://raw.githubusercontent.com/05bmckay/hs-uix/main/packages/form/assets/section-and-groups.png)
157
+
158
+ ### Custom Field Types
159
+
160
+ ![Custom Field Types](https://raw.githubusercontent.com/05bmckay/hs-uix/main/packages/form/assets/custom-field-types.png)
161
+
162
+ ### Display Options
163
+
164
+ ![Display Options](https://raw.githubusercontent.com/05bmckay/hs-uix/main/packages/form/assets/display-options.png)
165
+
166
+ ### Read-Only Mode
167
+
168
+ ![Read-Only Mode](https://raw.githubusercontent.com/05bmckay/hs-uix/main/packages/form/assets/readonly-autosave-dirty.png)
169
+
170
+ > **Full documentation:** [FormBuilder README](./packages/form/README.md) — includes full API reference, all field types, validation details, props tables, and more.
171
+
172
+ ---
173
+
174
+ ## Migrating from `@hs-uix/datatable` or `@hs-uix/form`
175
+
176
+ Both packages have been merged into `hs-uix`. Update your imports:
177
+
178
+ ```diff
179
+ - import { DataTable } from "@hs-uix/datatable";
180
+ + import { DataTable } from "hs-uix/datatable";
181
+
182
+ - import { FormBuilder } from "@hs-uix/form";
183
+ + import { FormBuilder } from "hs-uix/form";
74
184
  ```
75
- hs-uix/
76
- ├── packages/
77
- │ ├── datatable/ ← DataTable source + docs
78
- │ └── form/ ← FormBuilder source + docs
79
- ├── src/ ← unified package entry points
80
- └── package.json
185
+
186
+ ```bash
187
+ npm uninstall @hs-uix/datatable @hs-uix/form
188
+ npm install hs-uix
81
189
  ```
82
190
 
191
+ ---
192
+
83
193
  ## License
84
194
 
85
195
  MIT
package/dist/form.js CHANGED
@@ -945,14 +945,14 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
945
945
  const handleFieldBlur = (0, import_react.useCallback)(
946
946
  (name, value) => {
947
947
  if (!validateOnBlur) return;
948
- const resolvedValue = value != null ? value : formValues[name];
948
+ const resolvedValue = value != null ? value : formValuesRef.current[name];
949
949
  const err = validateField(name, resolvedValue);
950
950
  updateErrors({ [name]: err });
951
951
  if (!err) {
952
952
  triggerAsyncValidation(name, resolvedValue);
953
953
  }
954
954
  },
955
- [validateOnBlur, validateField, updateErrors, formValues, triggerAsyncValidation]
955
+ [validateOnBlur, validateField, updateErrors, triggerAsyncValidation]
956
956
  );
957
957
  const handleSubmit = (0, import_react.useCallback)(
958
958
  async (e) => {
package/dist/form.mjs CHANGED
@@ -949,14 +949,14 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
949
949
  const handleFieldBlur = useCallback(
950
950
  (name, value) => {
951
951
  if (!validateOnBlur) return;
952
- const resolvedValue = value != null ? value : formValues[name];
952
+ const resolvedValue = value != null ? value : formValuesRef.current[name];
953
953
  const err = validateField(name, resolvedValue);
954
954
  updateErrors({ [name]: err });
955
955
  if (!err) {
956
956
  triggerAsyncValidation(name, resolvedValue);
957
957
  }
958
958
  },
959
- [validateOnBlur, validateField, updateErrors, formValues, triggerAsyncValidation]
959
+ [validateOnBlur, validateField, updateErrors, triggerAsyncValidation]
960
960
  );
961
961
  const handleSubmit = useCallback(
962
962
  async (e) => {
package/dist/index.js CHANGED
@@ -1948,14 +1948,14 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
1948
1948
  const handleFieldBlur = (0, import_react2.useCallback)(
1949
1949
  (name, value) => {
1950
1950
  if (!validateOnBlur) return;
1951
- const resolvedValue = value != null ? value : formValues[name];
1951
+ const resolvedValue = value != null ? value : formValuesRef.current[name];
1952
1952
  const err = validateField(name, resolvedValue);
1953
1953
  updateErrors({ [name]: err });
1954
1954
  if (!err) {
1955
1955
  triggerAsyncValidation(name, resolvedValue);
1956
1956
  }
1957
1957
  },
1958
- [validateOnBlur, validateField, updateErrors, formValues, triggerAsyncValidation]
1958
+ [validateOnBlur, validateField, updateErrors, triggerAsyncValidation]
1959
1959
  );
1960
1960
  const handleSubmit = (0, import_react2.useCallback)(
1961
1961
  async (e) => {
package/dist/index.mjs CHANGED
@@ -1981,14 +1981,14 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1981
1981
  const handleFieldBlur = useCallback2(
1982
1982
  (name, value) => {
1983
1983
  if (!validateOnBlur) return;
1984
- const resolvedValue = value != null ? value : formValues[name];
1984
+ const resolvedValue = value != null ? value : formValuesRef.current[name];
1985
1985
  const err = validateField(name, resolvedValue);
1986
1986
  updateErrors({ [name]: err });
1987
1987
  if (!err) {
1988
1988
  triggerAsyncValidation(name, resolvedValue);
1989
1989
  }
1990
1990
  },
1991
- [validateOnBlur, validateField, updateErrors, formValues, triggerAsyncValidation]
1991
+ [validateOnBlur, validateField, updateErrors, triggerAsyncValidation]
1992
1992
  );
1993
1993
  const handleSubmit = useCallback2(
1994
1994
  async (e) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hs-uix",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Production-ready UI components for HubSpot UI Extensions — DataTable, FormBuilder, and more",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.js",