@nucleux/md3-toolbar 0.0.0-stage → 0.6.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.
- package/LICENSE +21 -0
- package/README.md +37 -2
- package/dist/index.cjs +180 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +36 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +157 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nucleux
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,38 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @nucleux/md3-toolbar
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Material 3 docked and floating toolbar — Nucleux agentic UI for React.
|
|
4
|
+
|
|
5
|
+
Part of **[Nucleux](https://github.com/intelligaia-open-labs/nucleux)** — agentic UI components for React. Install a single component, or the full kit via [`@nucleux/react`](https://www.npmjs.com/package/@nucleux/react).
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## Exports
|
|
9
|
+
|
|
10
|
+
- **`Md3Toolbar`** — `role="toolbar"` with arrow-key roving focus (`type="docked" | "floating"`, `orientation="horizontal" | "vertical"` for floating, `color="standard" | "vibrant"`, `loop`)
|
|
11
|
+
- **`Md3ToolbarIconButton`** — an `Md3IconButton` coloured for the toolbar; `selected` for toggled actions
|
|
12
|
+
- Types: `Md3ToolbarProps`, `Md3ToolbarIconButtonProps`, `Md3ToolbarType`, `Md3ToolbarOrientation`, `Md3ToolbarColor`
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add @nucleux/md3-toolbar @nucleux/tokens
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`react` / `react-dom` are peer dependencies.
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import { Md3Toolbar, Md3ToolbarIconButton } from "@nucleux/md3-toolbar";
|
|
26
|
+
|
|
27
|
+
<Md3Toolbar type="floating" color="vibrant" aria-label="Text formatting">
|
|
28
|
+
<Md3ToolbarIconButton aria-label="Bold" selected><Bold /></Md3ToolbarIconButton>
|
|
29
|
+
<Md3ToolbarIconButton aria-label="Italic"><Italic /></Md3ToolbarIconButton>
|
|
30
|
+
</Md3Toolbar>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Requires the theme once in your app: `import "@nucleux/tokens/styles.css";`
|
|
34
|
+
and the Tailwind preset from `@nucleux/tokens/preset`.
|
|
35
|
+
|
|
36
|
+
## License
|
|
37
|
+
|
|
38
|
+
MIT © Nucleux
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var React = require('react');
|
|
5
|
+
var md3IconButton = require('@nucleux/md3-icon-button');
|
|
6
|
+
var utils = require('@nucleux/utils');
|
|
7
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
8
|
+
|
|
9
|
+
function _interopNamespace(e) {
|
|
10
|
+
if (e && e.__esModule) return e;
|
|
11
|
+
var n = Object.create(null);
|
|
12
|
+
if (e) {
|
|
13
|
+
Object.keys(e).forEach(function (k) {
|
|
14
|
+
if (k !== 'default') {
|
|
15
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
16
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
get: function () { return e[k]; }
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
n.default = e;
|
|
24
|
+
return Object.freeze(n);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
28
|
+
|
|
29
|
+
// src/index.tsx
|
|
30
|
+
var ToolbarContext = React__namespace.createContext({ color: "standard" });
|
|
31
|
+
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React__namespace.useLayoutEffect : React__namespace.useEffect;
|
|
32
|
+
var ITEM_SELECTOR = [
|
|
33
|
+
"button",
|
|
34
|
+
"a[href]",
|
|
35
|
+
"input",
|
|
36
|
+
"select",
|
|
37
|
+
"textarea",
|
|
38
|
+
'[role="button"]',
|
|
39
|
+
'[role="checkbox"]',
|
|
40
|
+
'[role="radio"]',
|
|
41
|
+
'[role="switch"]',
|
|
42
|
+
'[role="combobox"]',
|
|
43
|
+
"[data-md3-toolbar-item]"
|
|
44
|
+
].join(",");
|
|
45
|
+
var getItems = (root) => Array.from(root.querySelectorAll(ITEM_SELECTOR)).filter((el) => {
|
|
46
|
+
if (el.hasAttribute("disabled") || el.getAttribute("aria-disabled") === "true") return false;
|
|
47
|
+
if (el.type === "hidden") return false;
|
|
48
|
+
const popup = el.parentElement?.closest('[role="menu"], [role="dialog"], [role="listbox"], [role="toolbar"]');
|
|
49
|
+
return !popup || popup === root;
|
|
50
|
+
});
|
|
51
|
+
var isTextEntry = (el) => el instanceof HTMLElement && (el.isContentEditable || el.tagName === "TEXTAREA" || el.tagName === "SELECT" || el.tagName === "INPUT" && !["button", "checkbox", "radio", "submit", "reset", "image"].includes(el.type));
|
|
52
|
+
var containers = {
|
|
53
|
+
standard: "bg-md-surface-container text-md-on-surface-variant",
|
|
54
|
+
vibrant: "bg-md-primary-container text-md-on-primary-container"
|
|
55
|
+
};
|
|
56
|
+
var Md3Toolbar = React__namespace.forwardRef(
|
|
57
|
+
({
|
|
58
|
+
type = "docked",
|
|
59
|
+
orientation: orientationProp = "horizontal",
|
|
60
|
+
color = "standard",
|
|
61
|
+
loop = true,
|
|
62
|
+
className,
|
|
63
|
+
children,
|
|
64
|
+
onKeyDown,
|
|
65
|
+
onFocus,
|
|
66
|
+
...props
|
|
67
|
+
}, ref) => {
|
|
68
|
+
const orientation = type === "docked" ? "horizontal" : orientationProp;
|
|
69
|
+
const rootRef = React__namespace.useRef(null);
|
|
70
|
+
const active = React__namespace.useRef(0);
|
|
71
|
+
const sync = React__namespace.useCallback(() => {
|
|
72
|
+
const root = rootRef.current;
|
|
73
|
+
if (!root) return;
|
|
74
|
+
const items = getItems(root);
|
|
75
|
+
if (items.length === 0) return;
|
|
76
|
+
if (active.current >= items.length) active.current = items.length - 1;
|
|
77
|
+
items.forEach((el, i) => {
|
|
78
|
+
el.tabIndex = i === active.current ? 0 : -1;
|
|
79
|
+
});
|
|
80
|
+
}, []);
|
|
81
|
+
useIsomorphicLayoutEffect(sync);
|
|
82
|
+
React__namespace.useEffect(() => {
|
|
83
|
+
const root = rootRef.current;
|
|
84
|
+
if (!root || typeof MutationObserver === "undefined") return;
|
|
85
|
+
const observer = new MutationObserver(sync);
|
|
86
|
+
observer.observe(root, {
|
|
87
|
+
childList: true,
|
|
88
|
+
subtree: true,
|
|
89
|
+
attributes: true,
|
|
90
|
+
attributeFilter: ["disabled", "aria-disabled"]
|
|
91
|
+
});
|
|
92
|
+
return () => observer.disconnect();
|
|
93
|
+
}, [sync]);
|
|
94
|
+
const handleFocus = (e) => {
|
|
95
|
+
onFocus?.(e);
|
|
96
|
+
const root = rootRef.current;
|
|
97
|
+
if (!root) return;
|
|
98
|
+
const index = getItems(root).indexOf(e.target);
|
|
99
|
+
if (index >= 0 && index !== active.current) {
|
|
100
|
+
active.current = index;
|
|
101
|
+
sync();
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
const handleKeyDown = (e) => {
|
|
105
|
+
onKeyDown?.(e);
|
|
106
|
+
const root = rootRef.current;
|
|
107
|
+
if (e.defaultPrevented || !root) return;
|
|
108
|
+
const items = getItems(root);
|
|
109
|
+
const index = items.indexOf(e.target);
|
|
110
|
+
if (index < 0) return;
|
|
111
|
+
const rtl = getComputedStyle(root).direction === "rtl";
|
|
112
|
+
const horizontal = orientation === "horizontal";
|
|
113
|
+
const nextKey = horizontal ? rtl ? "ArrowLeft" : "ArrowRight" : "ArrowDown";
|
|
114
|
+
const prevKey = horizontal ? rtl ? "ArrowRight" : "ArrowLeft" : "ArrowUp";
|
|
115
|
+
if (horizontal && isTextEntry(e.target) && (e.key === "ArrowLeft" || e.key === "ArrowRight")) return;
|
|
116
|
+
let next;
|
|
117
|
+
if (e.key === nextKey) next = index + 1 >= items.length ? loop ? 0 : index : index + 1;
|
|
118
|
+
else if (e.key === prevKey) next = index - 1 < 0 ? loop ? items.length - 1 : index : index - 1;
|
|
119
|
+
else if (e.key === "Home" && !isTextEntry(e.target)) next = 0;
|
|
120
|
+
else if (e.key === "End" && !isTextEntry(e.target)) next = items.length - 1;
|
|
121
|
+
else return;
|
|
122
|
+
e.preventDefault();
|
|
123
|
+
active.current = next;
|
|
124
|
+
sync();
|
|
125
|
+
items[next].focus();
|
|
126
|
+
};
|
|
127
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
128
|
+
"div",
|
|
129
|
+
{
|
|
130
|
+
ref: (node) => {
|
|
131
|
+
rootRef.current = node;
|
|
132
|
+
if (typeof ref === "function") ref(node);
|
|
133
|
+
else if (ref) ref.current = node;
|
|
134
|
+
},
|
|
135
|
+
role: "toolbar",
|
|
136
|
+
"aria-orientation": orientation,
|
|
137
|
+
"data-type": type,
|
|
138
|
+
"data-orientation": orientation,
|
|
139
|
+
onKeyDown: handleKeyDown,
|
|
140
|
+
onFocus: handleFocus,
|
|
141
|
+
className: utils.cn(
|
|
142
|
+
"flex items-center",
|
|
143
|
+
containers[color],
|
|
144
|
+
type === "docked" && "h-16 w-full justify-evenly gap-2 px-4",
|
|
145
|
+
type === "floating" && "inline-flex gap-1 rounded-full shadow-md-3",
|
|
146
|
+
type === "floating" && (orientation === "horizontal" ? "h-16 px-2" : "w-16 flex-col py-2"),
|
|
147
|
+
className
|
|
148
|
+
),
|
|
149
|
+
...props,
|
|
150
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ToolbarContext.Provider, { value: { color }, children })
|
|
151
|
+
}
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
);
|
|
155
|
+
Md3Toolbar.displayName = "Md3Toolbar";
|
|
156
|
+
var iconButtonColors = {
|
|
157
|
+
standard: ["text-md-on-surface-variant", "bg-md-secondary-container text-md-on-secondary-container"],
|
|
158
|
+
vibrant: ["text-md-on-primary-container", "bg-md-surface-container text-md-on-surface"]
|
|
159
|
+
};
|
|
160
|
+
var Md3ToolbarIconButton = React__namespace.forwardRef(
|
|
161
|
+
({ selected = false, className, ...props }, ref) => {
|
|
162
|
+
const { color } = React__namespace.useContext(ToolbarContext);
|
|
163
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
164
|
+
md3IconButton.Md3IconButton,
|
|
165
|
+
{
|
|
166
|
+
ref,
|
|
167
|
+
variant: "standard",
|
|
168
|
+
selected,
|
|
169
|
+
className: utils.cn("h-12 w-12", iconButtonColors[color][selected ? 1 : 0], className),
|
|
170
|
+
...props
|
|
171
|
+
}
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
);
|
|
175
|
+
Md3ToolbarIconButton.displayName = "Md3ToolbarIconButton";
|
|
176
|
+
|
|
177
|
+
exports.Md3Toolbar = Md3Toolbar;
|
|
178
|
+
exports.Md3ToolbarIconButton = Md3ToolbarIconButton;
|
|
179
|
+
//# sourceMappingURL=index.cjs.map
|
|
180
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.tsx"],"names":["React","jsx","cn","Md3IconButton"],"mappings":"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","file":"index.cjs","sourcesContent":["import * as React from \"react\";\nimport { Md3IconButton, type Md3IconButtonProps } from \"@nucleux/md3-icon-button\";\nimport { cn } from \"@nucleux/utils\";\n\nexport type Md3ToolbarType = \"docked\" | \"floating\";\nexport type Md3ToolbarOrientation = \"horizontal\" | \"vertical\";\nexport type Md3ToolbarColor = \"standard\" | \"vibrant\";\n\ninterface ToolbarContextValue {\n color: Md3ToolbarColor;\n}\nconst ToolbarContext = React.createContext<ToolbarContextValue>({ color: \"standard\" });\n\nexport interface Md3ToolbarProps extends React.HTMLAttributes<HTMLDivElement> {\n /** `docked` spans the width of its container (replaces the bottom app bar); `floating` is a pill above content. */\n type?: Md3ToolbarType;\n /** Layout of a floating toolbar. Docked toolbars are always horizontal. */\n orientation?: Md3ToolbarOrientation;\n /** `standard` uses surface-container; `vibrant` uses primary-container for more emphasis. */\n color?: Md3ToolbarColor;\n /** Whether arrow keys wrap from the last control to the first and back. */\n loop?: boolean;\n}\n\nconst useIsomorphicLayoutEffect = typeof window !== \"undefined\" ? React.useLayoutEffect : React.useEffect;\n\nconst ITEM_SELECTOR = [\n \"button\",\n \"a[href]\",\n \"input\",\n \"select\",\n \"textarea\",\n '[role=\"button\"]',\n '[role=\"checkbox\"]',\n '[role=\"radio\"]',\n '[role=\"switch\"]',\n '[role=\"combobox\"]',\n \"[data-md3-toolbar-item]\",\n].join(\",\");\n\n/** Focusable controls of the toolbar, skipping disabled ones and anything inside a popup (menu, dialog, listbox). */\nconst getItems = (root: HTMLElement) =>\n Array.from(root.querySelectorAll<HTMLElement>(ITEM_SELECTOR)).filter((el) => {\n if (el.hasAttribute(\"disabled\") || el.getAttribute(\"aria-disabled\") === \"true\") return false;\n if ((el as HTMLInputElement).type === \"hidden\") return false;\n const popup = el.parentElement?.closest('[role=\"menu\"], [role=\"dialog\"], [role=\"listbox\"], [role=\"toolbar\"]');\n return !popup || popup === root;\n });\n\nconst isTextEntry = (el: EventTarget | null) =>\n el instanceof HTMLElement &&\n (el.isContentEditable ||\n el.tagName === \"TEXTAREA\" ||\n el.tagName === \"SELECT\" ||\n (el.tagName === \"INPUT\" &&\n ![\"button\", \"checkbox\", \"radio\", \"submit\", \"reset\", \"image\"].includes((el as HTMLInputElement).type)));\n\nconst containers: Record<Md3ToolbarColor, string> = {\n standard: \"bg-md-surface-container text-md-on-surface-variant\",\n vibrant: \"bg-md-primary-container text-md-on-primary-container\",\n};\n\n/**\n * A Material 3 Expressive toolbar for actions related to the current page. The\n * `docked` type spans the bottom (or top) of the screen at 64px tall and replaces\n * the bottom app bar; the `floating` type is an elevated, fully rounded container\n * that floats over content in a horizontal or vertical orientation and pairs well\n * with an Md3Fab placed beside it. Both come in standard and vibrant colours. It\n * implements the WAI-ARIA toolbar pattern: one Tab stop, with arrow keys (Left/Right,\n * or Up/Down when vertical), Home and End moving focus between its controls.\n * Give it an `aria-label`, and use Md3ToolbarIconButton for colour-matched icon buttons.\n */\nexport const Md3Toolbar = React.forwardRef<HTMLDivElement, Md3ToolbarProps>(\n (\n {\n type = \"docked\",\n orientation: orientationProp = \"horizontal\",\n color = \"standard\",\n loop = true,\n className,\n children,\n onKeyDown,\n onFocus,\n ...props\n },\n ref,\n ) => {\n const orientation = type === \"docked\" ? \"horizontal\" : orientationProp;\n const rootRef = React.useRef<HTMLDivElement | null>(null);\n const active = React.useRef(0);\n\n // Roving tabindex: exactly one control is in the Tab order.\n const sync = React.useCallback(() => {\n const root = rootRef.current;\n if (!root) return;\n const items = getItems(root);\n if (items.length === 0) return;\n if (active.current >= items.length) active.current = items.length - 1;\n items.forEach((el, i) => {\n el.tabIndex = i === active.current ? 0 : -1;\n });\n }, []);\n\n useIsomorphicLayoutEffect(sync);\n\n React.useEffect(() => {\n const root = rootRef.current;\n if (!root || typeof MutationObserver === \"undefined\") return;\n const observer = new MutationObserver(sync);\n observer.observe(root, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\"disabled\", \"aria-disabled\"],\n });\n return () => observer.disconnect();\n }, [sync]);\n\n const handleFocus = (e: React.FocusEvent<HTMLDivElement>) => {\n onFocus?.(e);\n const root = rootRef.current;\n if (!root) return;\n const index = getItems(root).indexOf(e.target as HTMLElement);\n if (index >= 0 && index !== active.current) {\n active.current = index;\n sync();\n }\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(e);\n const root = rootRef.current;\n if (e.defaultPrevented || !root) return;\n const items = getItems(root);\n const index = items.indexOf(e.target as HTMLElement);\n if (index < 0) return;\n\n const rtl = getComputedStyle(root).direction === \"rtl\";\n const horizontal = orientation === \"horizontal\";\n const nextKey = horizontal ? (rtl ? \"ArrowLeft\" : \"ArrowRight\") : \"ArrowDown\";\n const prevKey = horizontal ? (rtl ? \"ArrowRight\" : \"ArrowLeft\") : \"ArrowUp\";\n // Leave caret movement to text fields.\n if (horizontal && isTextEntry(e.target) && (e.key === \"ArrowLeft\" || e.key === \"ArrowRight\")) return;\n\n let next: number;\n if (e.key === nextKey) next = index + 1 >= items.length ? (loop ? 0 : index) : index + 1;\n else if (e.key === prevKey) next = index - 1 < 0 ? (loop ? items.length - 1 : index) : index - 1;\n else if (e.key === \"Home\" && !isTextEntry(e.target)) next = 0;\n else if (e.key === \"End\" && !isTextEntry(e.target)) next = items.length - 1;\n else return;\n\n e.preventDefault();\n active.current = next;\n sync();\n items[next].focus();\n };\n\n return (\n <div\n ref={(node) => {\n rootRef.current = node;\n if (typeof ref === \"function\") ref(node);\n else if (ref) ref.current = node;\n }}\n role=\"toolbar\"\n aria-orientation={orientation}\n data-type={type}\n data-orientation={orientation}\n onKeyDown={handleKeyDown}\n onFocus={handleFocus}\n className={cn(\n \"flex items-center\",\n containers[color],\n type === \"docked\" && \"h-16 w-full justify-evenly gap-2 px-4\",\n type === \"floating\" && \"inline-flex gap-1 rounded-full shadow-md-3\",\n type === \"floating\" && (orientation === \"horizontal\" ? \"h-16 px-2\" : \"w-16 flex-col py-2\"),\n className,\n )}\n {...props}\n >\n <ToolbarContext.Provider value={{ color }}>{children}</ToolbarContext.Provider>\n </div>\n );\n },\n);\nMd3Toolbar.displayName = \"Md3Toolbar\";\n\nexport type Md3ToolbarIconButtonProps = Omit<Md3IconButtonProps, \"variant\">;\n\nconst iconButtonColors: Record<Md3ToolbarColor, [string, string]> = {\n standard: [\"text-md-on-surface-variant\", \"bg-md-secondary-container text-md-on-secondary-container\"],\n vibrant: [\"text-md-on-primary-container\", \"bg-md-surface-container text-md-on-surface\"],\n};\n\n/**\n * An Md3IconButton coloured for the surrounding Md3Toolbar (standard or vibrant).\n * Set `selected` for a toggled action; it then shows a contrasting container and\n * exposes `aria-pressed`.\n */\nexport const Md3ToolbarIconButton = React.forwardRef<HTMLButtonElement, Md3ToolbarIconButtonProps>(\n ({ selected = false, className, ...props }, ref) => {\n const { color } = React.useContext(ToolbarContext);\n return (\n <Md3IconButton\n ref={ref}\n variant=\"standard\"\n selected={selected}\n className={cn(\"h-12 w-12\", iconButtonColors[color][selected ? 1 : 0], className)}\n {...props}\n />\n );\n },\n);\nMd3ToolbarIconButton.displayName = \"Md3ToolbarIconButton\";\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Md3IconButtonProps } from '@nucleux/md3-icon-button';
|
|
3
|
+
|
|
4
|
+
type Md3ToolbarType = "docked" | "floating";
|
|
5
|
+
type Md3ToolbarOrientation = "horizontal" | "vertical";
|
|
6
|
+
type Md3ToolbarColor = "standard" | "vibrant";
|
|
7
|
+
interface Md3ToolbarProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
8
|
+
/** `docked` spans the width of its container (replaces the bottom app bar); `floating` is a pill above content. */
|
|
9
|
+
type?: Md3ToolbarType;
|
|
10
|
+
/** Layout of a floating toolbar. Docked toolbars are always horizontal. */
|
|
11
|
+
orientation?: Md3ToolbarOrientation;
|
|
12
|
+
/** `standard` uses surface-container; `vibrant` uses primary-container for more emphasis. */
|
|
13
|
+
color?: Md3ToolbarColor;
|
|
14
|
+
/** Whether arrow keys wrap from the last control to the first and back. */
|
|
15
|
+
loop?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A Material 3 Expressive toolbar for actions related to the current page. The
|
|
19
|
+
* `docked` type spans the bottom (or top) of the screen at 64px tall and replaces
|
|
20
|
+
* the bottom app bar; the `floating` type is an elevated, fully rounded container
|
|
21
|
+
* that floats over content in a horizontal or vertical orientation and pairs well
|
|
22
|
+
* with an Md3Fab placed beside it. Both come in standard and vibrant colours. It
|
|
23
|
+
* implements the WAI-ARIA toolbar pattern: one Tab stop, with arrow keys (Left/Right,
|
|
24
|
+
* or Up/Down when vertical), Home and End moving focus between its controls.
|
|
25
|
+
* Give it an `aria-label`, and use Md3ToolbarIconButton for colour-matched icon buttons.
|
|
26
|
+
*/
|
|
27
|
+
declare const Md3Toolbar: React.ForwardRefExoticComponent<Md3ToolbarProps & React.RefAttributes<HTMLDivElement>>;
|
|
28
|
+
type Md3ToolbarIconButtonProps = Omit<Md3IconButtonProps, "variant">;
|
|
29
|
+
/**
|
|
30
|
+
* An Md3IconButton coloured for the surrounding Md3Toolbar (standard or vibrant).
|
|
31
|
+
* Set `selected` for a toggled action; it then shows a contrasting container and
|
|
32
|
+
* exposes `aria-pressed`.
|
|
33
|
+
*/
|
|
34
|
+
declare const Md3ToolbarIconButton: React.ForwardRefExoticComponent<Md3ToolbarIconButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
35
|
+
|
|
36
|
+
export { Md3Toolbar, type Md3ToolbarColor, Md3ToolbarIconButton, type Md3ToolbarIconButtonProps, type Md3ToolbarOrientation, type Md3ToolbarProps, type Md3ToolbarType };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Md3IconButtonProps } from '@nucleux/md3-icon-button';
|
|
3
|
+
|
|
4
|
+
type Md3ToolbarType = "docked" | "floating";
|
|
5
|
+
type Md3ToolbarOrientation = "horizontal" | "vertical";
|
|
6
|
+
type Md3ToolbarColor = "standard" | "vibrant";
|
|
7
|
+
interface Md3ToolbarProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
8
|
+
/** `docked` spans the width of its container (replaces the bottom app bar); `floating` is a pill above content. */
|
|
9
|
+
type?: Md3ToolbarType;
|
|
10
|
+
/** Layout of a floating toolbar. Docked toolbars are always horizontal. */
|
|
11
|
+
orientation?: Md3ToolbarOrientation;
|
|
12
|
+
/** `standard` uses surface-container; `vibrant` uses primary-container for more emphasis. */
|
|
13
|
+
color?: Md3ToolbarColor;
|
|
14
|
+
/** Whether arrow keys wrap from the last control to the first and back. */
|
|
15
|
+
loop?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A Material 3 Expressive toolbar for actions related to the current page. The
|
|
19
|
+
* `docked` type spans the bottom (or top) of the screen at 64px tall and replaces
|
|
20
|
+
* the bottom app bar; the `floating` type is an elevated, fully rounded container
|
|
21
|
+
* that floats over content in a horizontal or vertical orientation and pairs well
|
|
22
|
+
* with an Md3Fab placed beside it. Both come in standard and vibrant colours. It
|
|
23
|
+
* implements the WAI-ARIA toolbar pattern: one Tab stop, with arrow keys (Left/Right,
|
|
24
|
+
* or Up/Down when vertical), Home and End moving focus between its controls.
|
|
25
|
+
* Give it an `aria-label`, and use Md3ToolbarIconButton for colour-matched icon buttons.
|
|
26
|
+
*/
|
|
27
|
+
declare const Md3Toolbar: React.ForwardRefExoticComponent<Md3ToolbarProps & React.RefAttributes<HTMLDivElement>>;
|
|
28
|
+
type Md3ToolbarIconButtonProps = Omit<Md3IconButtonProps, "variant">;
|
|
29
|
+
/**
|
|
30
|
+
* An Md3IconButton coloured for the surrounding Md3Toolbar (standard or vibrant).
|
|
31
|
+
* Set `selected` for a toggled action; it then shows a contrasting container and
|
|
32
|
+
* exposes `aria-pressed`.
|
|
33
|
+
*/
|
|
34
|
+
declare const Md3ToolbarIconButton: React.ForwardRefExoticComponent<Md3ToolbarIconButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
35
|
+
|
|
36
|
+
export { Md3Toolbar, type Md3ToolbarColor, Md3ToolbarIconButton, type Md3ToolbarIconButtonProps, type Md3ToolbarOrientation, type Md3ToolbarProps, type Md3ToolbarType };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { Md3IconButton } from '@nucleux/md3-icon-button';
|
|
4
|
+
import { cn } from '@nucleux/utils';
|
|
5
|
+
import { jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
// src/index.tsx
|
|
8
|
+
var ToolbarContext = React.createContext({ color: "standard" });
|
|
9
|
+
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React.useLayoutEffect : React.useEffect;
|
|
10
|
+
var ITEM_SELECTOR = [
|
|
11
|
+
"button",
|
|
12
|
+
"a[href]",
|
|
13
|
+
"input",
|
|
14
|
+
"select",
|
|
15
|
+
"textarea",
|
|
16
|
+
'[role="button"]',
|
|
17
|
+
'[role="checkbox"]',
|
|
18
|
+
'[role="radio"]',
|
|
19
|
+
'[role="switch"]',
|
|
20
|
+
'[role="combobox"]',
|
|
21
|
+
"[data-md3-toolbar-item]"
|
|
22
|
+
].join(",");
|
|
23
|
+
var getItems = (root) => Array.from(root.querySelectorAll(ITEM_SELECTOR)).filter((el) => {
|
|
24
|
+
if (el.hasAttribute("disabled") || el.getAttribute("aria-disabled") === "true") return false;
|
|
25
|
+
if (el.type === "hidden") return false;
|
|
26
|
+
const popup = el.parentElement?.closest('[role="menu"], [role="dialog"], [role="listbox"], [role="toolbar"]');
|
|
27
|
+
return !popup || popup === root;
|
|
28
|
+
});
|
|
29
|
+
var isTextEntry = (el) => el instanceof HTMLElement && (el.isContentEditable || el.tagName === "TEXTAREA" || el.tagName === "SELECT" || el.tagName === "INPUT" && !["button", "checkbox", "radio", "submit", "reset", "image"].includes(el.type));
|
|
30
|
+
var containers = {
|
|
31
|
+
standard: "bg-md-surface-container text-md-on-surface-variant",
|
|
32
|
+
vibrant: "bg-md-primary-container text-md-on-primary-container"
|
|
33
|
+
};
|
|
34
|
+
var Md3Toolbar = React.forwardRef(
|
|
35
|
+
({
|
|
36
|
+
type = "docked",
|
|
37
|
+
orientation: orientationProp = "horizontal",
|
|
38
|
+
color = "standard",
|
|
39
|
+
loop = true,
|
|
40
|
+
className,
|
|
41
|
+
children,
|
|
42
|
+
onKeyDown,
|
|
43
|
+
onFocus,
|
|
44
|
+
...props
|
|
45
|
+
}, ref) => {
|
|
46
|
+
const orientation = type === "docked" ? "horizontal" : orientationProp;
|
|
47
|
+
const rootRef = React.useRef(null);
|
|
48
|
+
const active = React.useRef(0);
|
|
49
|
+
const sync = React.useCallback(() => {
|
|
50
|
+
const root = rootRef.current;
|
|
51
|
+
if (!root) return;
|
|
52
|
+
const items = getItems(root);
|
|
53
|
+
if (items.length === 0) return;
|
|
54
|
+
if (active.current >= items.length) active.current = items.length - 1;
|
|
55
|
+
items.forEach((el, i) => {
|
|
56
|
+
el.tabIndex = i === active.current ? 0 : -1;
|
|
57
|
+
});
|
|
58
|
+
}, []);
|
|
59
|
+
useIsomorphicLayoutEffect(sync);
|
|
60
|
+
React.useEffect(() => {
|
|
61
|
+
const root = rootRef.current;
|
|
62
|
+
if (!root || typeof MutationObserver === "undefined") return;
|
|
63
|
+
const observer = new MutationObserver(sync);
|
|
64
|
+
observer.observe(root, {
|
|
65
|
+
childList: true,
|
|
66
|
+
subtree: true,
|
|
67
|
+
attributes: true,
|
|
68
|
+
attributeFilter: ["disabled", "aria-disabled"]
|
|
69
|
+
});
|
|
70
|
+
return () => observer.disconnect();
|
|
71
|
+
}, [sync]);
|
|
72
|
+
const handleFocus = (e) => {
|
|
73
|
+
onFocus?.(e);
|
|
74
|
+
const root = rootRef.current;
|
|
75
|
+
if (!root) return;
|
|
76
|
+
const index = getItems(root).indexOf(e.target);
|
|
77
|
+
if (index >= 0 && index !== active.current) {
|
|
78
|
+
active.current = index;
|
|
79
|
+
sync();
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const handleKeyDown = (e) => {
|
|
83
|
+
onKeyDown?.(e);
|
|
84
|
+
const root = rootRef.current;
|
|
85
|
+
if (e.defaultPrevented || !root) return;
|
|
86
|
+
const items = getItems(root);
|
|
87
|
+
const index = items.indexOf(e.target);
|
|
88
|
+
if (index < 0) return;
|
|
89
|
+
const rtl = getComputedStyle(root).direction === "rtl";
|
|
90
|
+
const horizontal = orientation === "horizontal";
|
|
91
|
+
const nextKey = horizontal ? rtl ? "ArrowLeft" : "ArrowRight" : "ArrowDown";
|
|
92
|
+
const prevKey = horizontal ? rtl ? "ArrowRight" : "ArrowLeft" : "ArrowUp";
|
|
93
|
+
if (horizontal && isTextEntry(e.target) && (e.key === "ArrowLeft" || e.key === "ArrowRight")) return;
|
|
94
|
+
let next;
|
|
95
|
+
if (e.key === nextKey) next = index + 1 >= items.length ? loop ? 0 : index : index + 1;
|
|
96
|
+
else if (e.key === prevKey) next = index - 1 < 0 ? loop ? items.length - 1 : index : index - 1;
|
|
97
|
+
else if (e.key === "Home" && !isTextEntry(e.target)) next = 0;
|
|
98
|
+
else if (e.key === "End" && !isTextEntry(e.target)) next = items.length - 1;
|
|
99
|
+
else return;
|
|
100
|
+
e.preventDefault();
|
|
101
|
+
active.current = next;
|
|
102
|
+
sync();
|
|
103
|
+
items[next].focus();
|
|
104
|
+
};
|
|
105
|
+
return /* @__PURE__ */ jsx(
|
|
106
|
+
"div",
|
|
107
|
+
{
|
|
108
|
+
ref: (node) => {
|
|
109
|
+
rootRef.current = node;
|
|
110
|
+
if (typeof ref === "function") ref(node);
|
|
111
|
+
else if (ref) ref.current = node;
|
|
112
|
+
},
|
|
113
|
+
role: "toolbar",
|
|
114
|
+
"aria-orientation": orientation,
|
|
115
|
+
"data-type": type,
|
|
116
|
+
"data-orientation": orientation,
|
|
117
|
+
onKeyDown: handleKeyDown,
|
|
118
|
+
onFocus: handleFocus,
|
|
119
|
+
className: cn(
|
|
120
|
+
"flex items-center",
|
|
121
|
+
containers[color],
|
|
122
|
+
type === "docked" && "h-16 w-full justify-evenly gap-2 px-4",
|
|
123
|
+
type === "floating" && "inline-flex gap-1 rounded-full shadow-md-3",
|
|
124
|
+
type === "floating" && (orientation === "horizontal" ? "h-16 px-2" : "w-16 flex-col py-2"),
|
|
125
|
+
className
|
|
126
|
+
),
|
|
127
|
+
...props,
|
|
128
|
+
children: /* @__PURE__ */ jsx(ToolbarContext.Provider, { value: { color }, children })
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
Md3Toolbar.displayName = "Md3Toolbar";
|
|
134
|
+
var iconButtonColors = {
|
|
135
|
+
standard: ["text-md-on-surface-variant", "bg-md-secondary-container text-md-on-secondary-container"],
|
|
136
|
+
vibrant: ["text-md-on-primary-container", "bg-md-surface-container text-md-on-surface"]
|
|
137
|
+
};
|
|
138
|
+
var Md3ToolbarIconButton = React.forwardRef(
|
|
139
|
+
({ selected = false, className, ...props }, ref) => {
|
|
140
|
+
const { color } = React.useContext(ToolbarContext);
|
|
141
|
+
return /* @__PURE__ */ jsx(
|
|
142
|
+
Md3IconButton,
|
|
143
|
+
{
|
|
144
|
+
ref,
|
|
145
|
+
variant: "standard",
|
|
146
|
+
selected,
|
|
147
|
+
className: cn("h-12 w-12", iconButtonColors[color][selected ? 1 : 0], className),
|
|
148
|
+
...props
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
);
|
|
153
|
+
Md3ToolbarIconButton.displayName = "Md3ToolbarIconButton";
|
|
154
|
+
|
|
155
|
+
export { Md3Toolbar, Md3ToolbarIconButton };
|
|
156
|
+
//# sourceMappingURL=index.js.map
|
|
157
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import * as React from \"react\";\nimport { Md3IconButton, type Md3IconButtonProps } from \"@nucleux/md3-icon-button\";\nimport { cn } from \"@nucleux/utils\";\n\nexport type Md3ToolbarType = \"docked\" | \"floating\";\nexport type Md3ToolbarOrientation = \"horizontal\" | \"vertical\";\nexport type Md3ToolbarColor = \"standard\" | \"vibrant\";\n\ninterface ToolbarContextValue {\n color: Md3ToolbarColor;\n}\nconst ToolbarContext = React.createContext<ToolbarContextValue>({ color: \"standard\" });\n\nexport interface Md3ToolbarProps extends React.HTMLAttributes<HTMLDivElement> {\n /** `docked` spans the width of its container (replaces the bottom app bar); `floating` is a pill above content. */\n type?: Md3ToolbarType;\n /** Layout of a floating toolbar. Docked toolbars are always horizontal. */\n orientation?: Md3ToolbarOrientation;\n /** `standard` uses surface-container; `vibrant` uses primary-container for more emphasis. */\n color?: Md3ToolbarColor;\n /** Whether arrow keys wrap from the last control to the first and back. */\n loop?: boolean;\n}\n\nconst useIsomorphicLayoutEffect = typeof window !== \"undefined\" ? React.useLayoutEffect : React.useEffect;\n\nconst ITEM_SELECTOR = [\n \"button\",\n \"a[href]\",\n \"input\",\n \"select\",\n \"textarea\",\n '[role=\"button\"]',\n '[role=\"checkbox\"]',\n '[role=\"radio\"]',\n '[role=\"switch\"]',\n '[role=\"combobox\"]',\n \"[data-md3-toolbar-item]\",\n].join(\",\");\n\n/** Focusable controls of the toolbar, skipping disabled ones and anything inside a popup (menu, dialog, listbox). */\nconst getItems = (root: HTMLElement) =>\n Array.from(root.querySelectorAll<HTMLElement>(ITEM_SELECTOR)).filter((el) => {\n if (el.hasAttribute(\"disabled\") || el.getAttribute(\"aria-disabled\") === \"true\") return false;\n if ((el as HTMLInputElement).type === \"hidden\") return false;\n const popup = el.parentElement?.closest('[role=\"menu\"], [role=\"dialog\"], [role=\"listbox\"], [role=\"toolbar\"]');\n return !popup || popup === root;\n });\n\nconst isTextEntry = (el: EventTarget | null) =>\n el instanceof HTMLElement &&\n (el.isContentEditable ||\n el.tagName === \"TEXTAREA\" ||\n el.tagName === \"SELECT\" ||\n (el.tagName === \"INPUT\" &&\n ![\"button\", \"checkbox\", \"radio\", \"submit\", \"reset\", \"image\"].includes((el as HTMLInputElement).type)));\n\nconst containers: Record<Md3ToolbarColor, string> = {\n standard: \"bg-md-surface-container text-md-on-surface-variant\",\n vibrant: \"bg-md-primary-container text-md-on-primary-container\",\n};\n\n/**\n * A Material 3 Expressive toolbar for actions related to the current page. The\n * `docked` type spans the bottom (or top) of the screen at 64px tall and replaces\n * the bottom app bar; the `floating` type is an elevated, fully rounded container\n * that floats over content in a horizontal or vertical orientation and pairs well\n * with an Md3Fab placed beside it. Both come in standard and vibrant colours. It\n * implements the WAI-ARIA toolbar pattern: one Tab stop, with arrow keys (Left/Right,\n * or Up/Down when vertical), Home and End moving focus between its controls.\n * Give it an `aria-label`, and use Md3ToolbarIconButton for colour-matched icon buttons.\n */\nexport const Md3Toolbar = React.forwardRef<HTMLDivElement, Md3ToolbarProps>(\n (\n {\n type = \"docked\",\n orientation: orientationProp = \"horizontal\",\n color = \"standard\",\n loop = true,\n className,\n children,\n onKeyDown,\n onFocus,\n ...props\n },\n ref,\n ) => {\n const orientation = type === \"docked\" ? \"horizontal\" : orientationProp;\n const rootRef = React.useRef<HTMLDivElement | null>(null);\n const active = React.useRef(0);\n\n // Roving tabindex: exactly one control is in the Tab order.\n const sync = React.useCallback(() => {\n const root = rootRef.current;\n if (!root) return;\n const items = getItems(root);\n if (items.length === 0) return;\n if (active.current >= items.length) active.current = items.length - 1;\n items.forEach((el, i) => {\n el.tabIndex = i === active.current ? 0 : -1;\n });\n }, []);\n\n useIsomorphicLayoutEffect(sync);\n\n React.useEffect(() => {\n const root = rootRef.current;\n if (!root || typeof MutationObserver === \"undefined\") return;\n const observer = new MutationObserver(sync);\n observer.observe(root, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\"disabled\", \"aria-disabled\"],\n });\n return () => observer.disconnect();\n }, [sync]);\n\n const handleFocus = (e: React.FocusEvent<HTMLDivElement>) => {\n onFocus?.(e);\n const root = rootRef.current;\n if (!root) return;\n const index = getItems(root).indexOf(e.target as HTMLElement);\n if (index >= 0 && index !== active.current) {\n active.current = index;\n sync();\n }\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(e);\n const root = rootRef.current;\n if (e.defaultPrevented || !root) return;\n const items = getItems(root);\n const index = items.indexOf(e.target as HTMLElement);\n if (index < 0) return;\n\n const rtl = getComputedStyle(root).direction === \"rtl\";\n const horizontal = orientation === \"horizontal\";\n const nextKey = horizontal ? (rtl ? \"ArrowLeft\" : \"ArrowRight\") : \"ArrowDown\";\n const prevKey = horizontal ? (rtl ? \"ArrowRight\" : \"ArrowLeft\") : \"ArrowUp\";\n // Leave caret movement to text fields.\n if (horizontal && isTextEntry(e.target) && (e.key === \"ArrowLeft\" || e.key === \"ArrowRight\")) return;\n\n let next: number;\n if (e.key === nextKey) next = index + 1 >= items.length ? (loop ? 0 : index) : index + 1;\n else if (e.key === prevKey) next = index - 1 < 0 ? (loop ? items.length - 1 : index) : index - 1;\n else if (e.key === \"Home\" && !isTextEntry(e.target)) next = 0;\n else if (e.key === \"End\" && !isTextEntry(e.target)) next = items.length - 1;\n else return;\n\n e.preventDefault();\n active.current = next;\n sync();\n items[next].focus();\n };\n\n return (\n <div\n ref={(node) => {\n rootRef.current = node;\n if (typeof ref === \"function\") ref(node);\n else if (ref) ref.current = node;\n }}\n role=\"toolbar\"\n aria-orientation={orientation}\n data-type={type}\n data-orientation={orientation}\n onKeyDown={handleKeyDown}\n onFocus={handleFocus}\n className={cn(\n \"flex items-center\",\n containers[color],\n type === \"docked\" && \"h-16 w-full justify-evenly gap-2 px-4\",\n type === \"floating\" && \"inline-flex gap-1 rounded-full shadow-md-3\",\n type === \"floating\" && (orientation === \"horizontal\" ? \"h-16 px-2\" : \"w-16 flex-col py-2\"),\n className,\n )}\n {...props}\n >\n <ToolbarContext.Provider value={{ color }}>{children}</ToolbarContext.Provider>\n </div>\n );\n },\n);\nMd3Toolbar.displayName = \"Md3Toolbar\";\n\nexport type Md3ToolbarIconButtonProps = Omit<Md3IconButtonProps, \"variant\">;\n\nconst iconButtonColors: Record<Md3ToolbarColor, [string, string]> = {\n standard: [\"text-md-on-surface-variant\", \"bg-md-secondary-container text-md-on-secondary-container\"],\n vibrant: [\"text-md-on-primary-container\", \"bg-md-surface-container text-md-on-surface\"],\n};\n\n/**\n * An Md3IconButton coloured for the surrounding Md3Toolbar (standard or vibrant).\n * Set `selected` for a toggled action; it then shows a contrasting container and\n * exposes `aria-pressed`.\n */\nexport const Md3ToolbarIconButton = React.forwardRef<HTMLButtonElement, Md3ToolbarIconButtonProps>(\n ({ selected = false, className, ...props }, ref) => {\n const { color } = React.useContext(ToolbarContext);\n return (\n <Md3IconButton\n ref={ref}\n variant=\"standard\"\n selected={selected}\n className={cn(\"h-12 w-12\", iconButtonColors[color][selected ? 1 : 0], className)}\n {...props}\n />\n );\n },\n);\nMd3ToolbarIconButton.displayName = \"Md3ToolbarIconButton\";\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,62 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nucleux/md3-toolbar",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"sideEffects": false,
|
|
6
|
+
"description": "Material 3 docked and floating toolbar — Nucleux agentic UI for React.",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"nucleux",
|
|
9
|
+
"react",
|
|
10
|
+
"ui",
|
|
11
|
+
"components",
|
|
12
|
+
"agentic",
|
|
13
|
+
"ai",
|
|
14
|
+
"tailwind",
|
|
15
|
+
"md3",
|
|
16
|
+
"material",
|
|
17
|
+
"toolbar",
|
|
18
|
+
"md3-toolbar"
|
|
19
|
+
],
|
|
20
|
+
"main": "./dist/index.cjs",
|
|
21
|
+
"module": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"import": "./dist/index.js",
|
|
27
|
+
"require": "./dist/index.cjs"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md"
|
|
33
|
+
],
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"react": ">=18",
|
|
36
|
+
"react-dom": ">=18"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@nucleux/md3-icon-button": "0.6.0",
|
|
40
|
+
"@nucleux/utils": "0.6.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"tsup": "^8.3.5",
|
|
44
|
+
"typescript": "^5.7.2",
|
|
45
|
+
"@types/react": "^18.3.18",
|
|
46
|
+
"react": "^18.3.1",
|
|
47
|
+
"lucide-react": "^0.469.0"
|
|
48
|
+
},
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
51
|
+
},
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
"repository": {
|
|
54
|
+
"type": "git",
|
|
55
|
+
"url": "git+https://github.com/intelligaia-open-labs/nucleux.git",
|
|
56
|
+
"directory": "packages/md3-toolbar"
|
|
57
|
+
},
|
|
58
|
+
"homepage": "https://github.com/intelligaia-open-labs/nucleux#readme",
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "tsup"
|
|
61
|
+
}
|
|
6
62
|
}
|