jattac.libs.web.zest-button 1.3.0 → 1.5.0

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.
@@ -18,6 +18,7 @@ This document provides an exhaustive reference for all `ZestButton` props and ty
18
18
  - [SuccessOptions](#successoptions)
19
19
  - [ConfirmOptions](#confirmoptions)
20
20
  - [SemanticType](#semantictype)
21
+ - [ZestDropdownOption](#zestdropdownoption)
21
22
 
22
23
  ---
23
24
 
@@ -50,6 +51,10 @@ This is the main configuration object passed to the `zest` prop.
50
51
  | `theme` | `'light' \| 'dark' \| 'system'` | `'system'` | Overrides the automatic theme detection. |
51
52
  | `buttonStyle` | `'solid' \| 'outline' \| 'text' \| 'dashed'`| `'solid'` | Defines the visual style of the button. |
52
53
  | `semanticType` | `SemanticType` | `undefined` | Defines the semantic type of the button, providing default visuals and behaviors. Extensible via module augmentation. |
54
+ | `dropdownOptions` | `ZestDropdownOption[]` | `undefined` | If provided (non-empty), renders the button as a split button: the main action on the left, and a chevron on the right that opens a menu of these options. See [`ZestDropdownOption`](#zestdropdownoption). |
55
+ | `dropdownAriaLabel` | `string` | `'More options'` | Accessible name for the chevron trigger button (it has no visible text). |
56
+ | `dropdownTheme` | `'light' \| 'dark' \| 'system'` | `'light'` | Theme for the dropdown menu panel, independent of the button's own `theme`. Menus default to light regardless of the button's theme; set this to theme the menu to match. |
57
+ | `dropdownWidth` | `number \| string` | `undefined` | Minimum width for the dropdown menu panel — a number is treated as pixels, a string is used as a raw CSS length (e.g. `'18rem'`). When unset, the menu's minimum width tracks the rendered width of the whole split button control. |
53
58
 
54
59
  ---
55
60
 
@@ -137,4 +142,24 @@ After augmentation, `'archive'` and `'publish'` would be valid `SemanticType` va
137
142
 
138
143
  ---
139
144
 
145
+ #### `ZestDropdownOption`
146
+
147
+ Describes a single secondary action in a split button's dropdown menu (see [`dropdownOptions`](#zestcustomprops)). Each option runs through the same busy/confirm machinery as the main button, independently per item.
148
+
149
+ | Prop Name | Type | Default | Description |
150
+ | :--- | :--- | :--- | :--- |
151
+ | `key` | `string` | index | Stable React key. Defaults to the option's index in the array if omitted. |
152
+ | `label` | `React.ReactNode` | **(Required)** | The menu item's visible content. |
153
+ | `icon` | `React.ReactNode` | `undefined` | An icon to display to the left of the label. |
154
+ | `disabled` | `boolean` | `false` | If true, the item cannot be selected. |
155
+ | `semanticType` | `SemanticType` | `undefined` | Same as the main button's `semanticType` — provides default icon/behavior for this item. |
156
+ | `onClick` | `(e: Event) => void \| Promise<void>` | `undefined` | Handler invoked when the item is selected. |
157
+ | `busyOptions` | `BusyOptions` | `{}` | Same shape as the main button's `busyOptions`, scoped to this item. |
158
+ | `successOptions` | `SuccessOptions` | `{}` | Same shape as the main button's `successOptions`, scoped to this item. |
159
+ | `confirmOptions` | `ConfirmOptions` | `undefined` | Same shape as the main button's `confirmOptions`, scoped to this item. |
160
+
161
+ *See the [Split Button with Overflow Actions recipe](./examples.md#recipe-5-a-split-button-with-overflow-actions) for a full example.*
162
+
163
+ ---
164
+
140
165
  [⬅️ Previous: Features Showcase](./features.md) | [Next: Configuration Guide ➡️](./configuration.md)
@@ -10,6 +10,7 @@ Welcome to the ZestButton Cookbook! This is the core learning path for mastering
10
10
  - [Recipe 2: The Safe "Delete" Button](#recipe-2-the-safe-delete-button)
11
11
  - [Recipe 3: Standardizing Your Buttons with a Global Config](#recipe-3-standardizing-your-buttons-with-a-global-config)
12
12
  - [Recipe 4: Creating a Custom "Archive" Button](#recipe-4-creating-a-custom-archive-button)
13
+ - [Recipe 5: A Split Button with Overflow Actions](#recipe-5-a-split-button-with-overflow-actions)
13
14
 
14
15
  ---
15
16
 
@@ -224,4 +225,65 @@ This powerful pattern allows you to build a complete, consistent design system f
224
225
 
225
226
  ---
226
227
 
228
+ ### Recipe 5: A Split Button with Overflow Actions
229
+
230
+ **Goal:** Create a button with one obvious default action ("Export as CSV") plus a few situational alternatives ("Export as PDF", "Export as JSON"), without cluttering the UI with three separate buttons.
231
+
232
+ **Problem:** You have a primary action and a handful of related, less-common actions. Placing them all as top-level buttons crowds the UI; hiding all of them behind a menu makes the primary action less discoverable and adds an extra click for the common case.
233
+
234
+ **Solution:** Pass `dropdownOptions` in `zest`. `ZestButton` renders as two visually-fused segments: the main action (left) fires immediately on click, exactly like a normal `ZestButton`, while the chevron (right) opens a menu of the extra options. Each option is independent — it can have its own icon, `busyOptions`, `successOptions`, or `confirmOptions`, running through the same machinery as the main button.
235
+
236
+ ```tsx
237
+ import React from 'react';
238
+ import ZestButton from 'jattac.libs.web.zest-button';
239
+ import { FaFileCsv, FaFilePdf, FaFileCode, FaTrash } from 'react-icons/fa6';
240
+
241
+ const ExportButton = () => {
242
+ const exportAs = (format: string) => async () => {
243
+ await new Promise((resolve) => setTimeout(resolve, 800));
244
+ alert(`Exported as ${format}!`);
245
+ };
246
+
247
+ return (
248
+ <ZestButton
249
+ onClick={exportAs('CSV')}
250
+ zest={{
251
+ visualOptions: { iconLeft: <FaFileCsv /> },
252
+ dropdownOptions: [
253
+ { label: 'Export as PDF', icon: <FaFilePdf />, onClick: exportAs('PDF') },
254
+ { label: 'Export as JSON', icon: <FaFileCode />, onClick: exportAs('JSON') },
255
+ {
256
+ label: 'Delete export history',
257
+ icon: <FaTrash />,
258
+ onClick: async () => alert('History cleared.'),
259
+ confirmOptions: { displayLabel: 'Confirm Delete', timeoutSecs: 5 },
260
+ },
261
+ ],
262
+ }}
263
+ >
264
+ Export as CSV
265
+ </ZestButton>
266
+ );
267
+ };
268
+ ```
269
+
270
+ **Theming and sizing the menu independently:** the dropdown menu panel defaults to a light theme and a minimum width matching the whole split button, regardless of the main button's own `theme`. Both are configurable, either per-button or app-wide via `ZestButtonConfigProvider`'s `defaultProps`:
271
+
272
+ ```tsx
273
+ <ZestButton
274
+ zest={{
275
+ theme: 'dark',
276
+ dropdownTheme: 'dark', // theme the menu to match, instead of the light default
277
+ dropdownWidth: '16rem', // force a specific minimum width instead of matching the button
278
+ dropdownOptions: [{ label: 'Export as PDF', onClick: exportAs('PDF') }],
279
+ }}
280
+ >
281
+ Export as CSV
282
+ </ZestButton>
283
+ ```
284
+
285
+ *For the full prop reference, see [`dropdownOptions`](./api.md#zestcustomprops) and [`ZestDropdownOption`](./api.md#zestdropdownoption) in our API reference.*
286
+
287
+ ---
288
+
227
289
  [⬅️ Previous: README](../README.md) | [Next: Features Showcase ➡️](./features.md)
@@ -11,6 +11,7 @@ This document provides a high-level showcase of what's possible with `ZestButton
11
11
  - [Semantic Types](#semantic-types)
12
12
  - [Global Configuration](#global-configuration)
13
13
  - [Rich Styling](#rich-styling)
14
+ - [Split Buttons & Overflow Menus](#split-buttons--overflow-menus)
14
15
 
15
16
  ---
16
17
 
@@ -123,4 +124,27 @@ const MyComponent = () => (
123
124
 
124
125
  ---
125
126
 
127
+ ### Split Buttons & Overflow Menus
128
+
129
+ **What it does:** Attaches a set of secondary actions to a button that already has one obvious default action, via a chevron segment that opens a menu — a discoverable place for "there's more here" without cluttering the UI with extra buttons. Each menu item gets its own independent busy/success/fail/confirm feedback, and the menu can be themed and sized independently of the main button.
130
+
131
+ ```tsx
132
+ const MyComponent = () => (
133
+ <ZestButton
134
+ onClick={() => alert('Exported as CSV!')}
135
+ zest={{
136
+ dropdownOptions: [
137
+ { label: 'Export as PDF', onClick: () => alert('Exported as PDF!') },
138
+ { label: 'Export as JSON', onClick: () => alert('Exported as JSON!') },
139
+ ],
140
+ }}
141
+ >
142
+ Export as CSV
143
+ </ZestButton>
144
+ );
145
+ ```
146
+ *__Learn more in the [Split Button with Overflow Actions recipe](./examples.md#recipe-5-a-split-button-with-overflow-actions).__*
147
+
148
+ ---
149
+
126
150
  [⬅️ Previous: The Cookbook (Examples)](./examples.md) | [Next: API Reference ➡️](./api.md)
package/package.json CHANGED
@@ -1,69 +1,79 @@
1
- {
2
- "name": "jattac.libs.web.zest-button",
3
- "version": "1.3.0",
4
- "description": "A highly customizable and production-ready React button component featuring robust asynchronous handling, rich visual feedback, and built-in confirmation flows for enhanced user experience",
5
- "homepage": "https://github.com/nyingimaina/jattac.libs.web.zest-button#readme",
6
- "repository": {
7
- "type": "git",
8
- "url": "git+https://github.com/nyingimaina/jattac.libs.web.zest-button.git"
9
- },
10
- "bugs": {
11
- "url": "https://github.com/nyingimaina/jattac.libs.web.zest-button/issues"
12
- },
13
- "main": "dist/index.cjs.js",
14
- "module": "dist/index.esm.js",
15
- "types": "dist/index.d.ts",
16
- "files": [
17
- "dist",
18
- "README.md",
19
- "docs"
20
- ],
21
- "scripts": {
22
- "build": "rollup -c rollup.config.mjs",
23
- "dev": "rollup -c rollup.config.mjs -w",
24
- "test": "echo \"No tests specified. See WORKPLAN.md for future plans.\" && exit 0"
25
- },
26
- "keywords": [
27
- "react",
28
- "button",
29
- "ui",
30
- "component",
31
- "interactive",
32
- "react-button",
33
- "form",
34
- "web",
35
- "async",
36
- "loading",
37
- "confirmation",
38
- "feedback",
39
- "animation",
40
- "transitions",
41
- "ux",
42
- "zest",
43
- "jattac"
44
- ],
45
- "author": "Jattac",
46
- "license": "MIT",
47
- "peerDependencies": {
48
- "react": ">=16.8.0",
49
- "react-dom": ">=16.8.0",
50
- "react-icons": "^5.0.1"
51
- },
52
- "devDependencies": {
53
- "@rollup/plugin-commonjs": "^25.0.7",
54
- "@rollup/plugin-node-resolve": "^15.2.3",
55
- "@rollup/plugin-typescript": "^11.1.6",
56
- "@types/node": "^25.1.0",
57
- "@types/react": "^18.2.67",
58
- "@types/react-dom": "^18.2.22",
59
- "postcss": "^8.4.38",
60
- "postcss-modules": "6.0.1",
61
- "react": "^18.2.0",
62
- "react-dom": "^18.2.0",
63
- "rollup": "^4.13.0",
64
- "rollup-plugin-dts": "^6.1.0",
65
- "rollup-plugin-postcss": "^4.0.2",
66
- "tslib": "^2.6.2",
67
- "typescript": "^5.4.2"
68
- }
69
- }
1
+ {
2
+ "name": "jattac.libs.web.zest-button",
3
+ "version": "1.5.0",
4
+ "description": "A highly customizable and production-ready React button component featuring robust asynchronous handling, rich visual feedback, and built-in confirmation flows for enhanced user experience",
5
+ "homepage": "https://github.com/nyingimaina/jattac.libs.web.zest-button#readme",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/nyingimaina/jattac.libs.web.zest-button.git"
9
+ },
10
+ "bugs": {
11
+ "url": "https://github.com/nyingimaina/jattac.libs.web.zest-button/issues"
12
+ },
13
+ "main": "dist/index.cjs.js",
14
+ "module": "dist/index.esm.js",
15
+ "types": "dist/index.d.ts",
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "LICENSE",
20
+ "documentation"
21
+ ],
22
+ "scripts": {
23
+ "build": "rollup -c rollup.config.mjs",
24
+ "dev": "rollup -c rollup.config.mjs -w",
25
+ "test": "jest --coverage"
26
+ },
27
+ "keywords": [
28
+ "react",
29
+ "button",
30
+ "ui",
31
+ "component",
32
+ "interactive",
33
+ "react-button",
34
+ "form",
35
+ "web",
36
+ "async",
37
+ "loading",
38
+ "confirmation",
39
+ "feedback",
40
+ "animation",
41
+ "transitions",
42
+ "ux",
43
+ "zest",
44
+ "jattac"
45
+ ],
46
+ "author": "Jattac",
47
+ "license": "MIT",
48
+ "peerDependencies": {
49
+ "@radix-ui/react-dropdown-menu": "^2.1.24",
50
+ "react": ">=16.8.0",
51
+ "react-dom": ">=16.8.0",
52
+ "react-icons": "^5.0.1"
53
+ },
54
+ "devDependencies": {
55
+ "@radix-ui/react-dropdown-menu": "^2.1.24",
56
+ "@rollup/plugin-commonjs": "^25.0.7",
57
+ "@rollup/plugin-node-resolve": "^15.2.3",
58
+ "@rollup/plugin-typescript": "^11.1.6",
59
+ "@testing-library/jest-dom": "^6.9.1",
60
+ "@testing-library/react": "^16.3.2",
61
+ "@types/jest": "^30.0.0",
62
+ "@types/node": "^25.1.0",
63
+ "@types/react": "^18.2.67",
64
+ "@types/react-dom": "^18.2.22",
65
+ "identity-obj-proxy": "^3.0.0",
66
+ "jest": "^30.4.2",
67
+ "jest-environment-jsdom": "^30.4.1",
68
+ "postcss": "^8.4.38",
69
+ "postcss-modules": "6.0.1",
70
+ "react": "^18.2.0",
71
+ "react-dom": "^18.2.0",
72
+ "rollup": "^4.13.0",
73
+ "rollup-plugin-dts": "^6.1.0",
74
+ "rollup-plugin-postcss": "^4.0.2",
75
+ "ts-jest": "^29.4.12",
76
+ "tslib": "^2.6.2",
77
+ "typescript": "^5.4.2"
78
+ }
79
+ }
@@ -1,9 +0,0 @@
1
- import React from 'react';
2
- import { ZestCustomProps } from './ZestButton';
3
- export interface ZestGlobalConfig {
4
- defaultProps?: ZestCustomProps;
5
- semanticTypeDefaults?: Partial<Record<string, Partial<ZestCustomProps>>>;
6
- }
7
- declare const ZestContext: React.Context<ZestGlobalConfig | undefined>;
8
- export declare const useZest: () => ZestGlobalConfig | undefined;
9
- export default ZestContext;
@@ -1,8 +0,0 @@
1
- import React from 'react';
2
- import { ZestGlobalConfig } from './ZestContext';
3
- interface ZestProviderProps {
4
- config: ZestGlobalConfig;
5
- children: React.ReactNode;
6
- }
7
- declare const ZestProvider: React.FC<ZestProviderProps>;
8
- export default ZestProvider;
@@ -1,4 +0,0 @@
1
- import { ZestCustomProps } from './ZestButton';
2
- type SemanticTypeDefaultsMap = Partial<Record<string, Partial<ZestCustomProps>>>;
3
- export declare const semanticTypeDefaults: SemanticTypeDefaultsMap;
4
- export {};
File without changes
File without changes
File without changes