@worknice/whiteboard 0.12.10 → 0.13.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.
@@ -16,16 +16,26 @@ type Props = {
16
16
  autoFocus?: boolean;
17
17
  disabled?: boolean;
18
18
  id?: string;
19
- options: Array<Option | null>;
20
19
  searchPlaceholder?: string;
21
20
  displaySearch?: boolean;
22
21
  iconRight?: ReactNode;
22
+ fullWidth?: boolean;
23
23
  } & ({
24
24
  children: ReactNode;
25
25
  icon?: ReactNode;
26
26
  } | {
27
27
  children?: ReactNode;
28
28
  icon: ReactNode;
29
+ }) & ({
30
+ options: Array<Option | null>;
31
+ optionGroups?: never;
32
+ } | {
33
+ options?: never;
34
+ optionGroups: Array<{
35
+ header: string;
36
+ options: Array<Option | null>;
37
+ }>;
29
38
  });
30
- declare const MenuButton: ({ autoFocus, disabled, id, options, searchPlaceholder, children, icon, iconRight, displaySearch, }: Props) => import("react/jsx-runtime").JSX.Element;
39
+ declare const MenuButton: ({ autoFocus, disabled, id, options, optionGroups, searchPlaceholder, children, icon, iconRight, displaySearch, fullWidth, }: Props) => import("react/jsx-runtime").JSX.Element;
31
40
  export default MenuButton;
41
+ export type { Option as MenuButtonOption };
@@ -16,18 +16,20 @@ import * as __WEBPACK_EXTERNAL_MODULE__utils_search_js_89d5ae26__ from "../utils
16
16
  import * as __WEBPACK_EXTERNAL_MODULE__utils_useNextContext_js_47529181__ from "../utils/useNextContext.js";
17
17
  import * as __WEBPACK_EXTERNAL_MODULE__Button_js_17bdd71d__ from "./Button.js";
18
18
  import * as __WEBPACK_EXTERNAL_MODULE__MenuButton_module_js_872b1a55__ from "./MenuButton.module.js";
19
- const MenuButton = ({ autoFocus = false, disabled = false, id, options, searchPlaceholder, children, icon, iconRight, displaySearch = false })=>{
19
+ const MenuButton = ({ autoFocus = false, disabled = false, id, options, optionGroups, searchPlaceholder, children, icon, iconRight, displaySearch = false, fullWidth = false })=>{
20
+ const listboxId = (0, __WEBPACK_EXTERNAL_MODULE_react__.useId)();
21
+ const generatedButtonId = (0, __WEBPACK_EXTERNAL_MODULE_react__.useId)();
22
+ const buttonId = id ?? generatedButtonId;
20
23
  const { Link: NextLink } = (0, __WEBPACK_EXTERNAL_MODULE__utils_useNextContext_js_47529181__["default"])();
21
24
  const optionsRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)([]);
22
25
  const searchRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(null);
23
- const listBoxRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(null);
24
26
  const [searchTerm, setSearchTerm] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)("");
25
27
  const [isOpen, setIsOpen] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)(false);
26
28
  const [optionToRender, setOptionToRender] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)(null);
27
29
  const [activeIndex, setActiveIndex] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)(null);
28
30
  const [loading, setLoading] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)(false);
29
31
  const [errors, setErrors] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)([]);
30
- const nonNullOptions = options.filter((option)=>null !== option);
32
+ const nonNullOptions = (options ?? optionGroups.flatMap((group)=>group.options) ?? []).filter((option)=>null !== option);
31
33
  const filteredOptions = "" !== searchTerm.trim() ? (0, __WEBPACK_EXTERNAL_MODULE__utils_search_js_89d5ae26__["default"])(nonNullOptions.map((option)=>({
32
34
  ...option,
33
35
  id: option.id
@@ -51,8 +53,7 @@ const MenuButton = ({ autoFocus = false, disabled = false, id, options, searchPl
51
53
  (0, __WEBPACK_EXTERNAL_MODULE__floating_ui_react_3ddd630a__.size)({
52
54
  apply ({ rects, elements }) {
53
55
  Object.assign(elements.floating.style, {
54
- minWidth: `${rects.reference.width}px`,
55
- maxWidth: rects.reference.width > 200 ? `${rects.reference.width}px` : "200px"
56
+ width: `${Math.max(rects.reference.width, 200)}px`
56
57
  });
57
58
  }
58
59
  })
@@ -71,17 +72,114 @@ const MenuButton = ({ autoFocus = false, disabled = false, id, options, searchPl
71
72
  dismiss,
72
73
  listNav
73
74
  ]);
75
+ const renderOption = (0, __WEBPACK_EXTERNAL_MODULE_react__.useCallback)((option, index)=>{
76
+ if ("link" === option.type) return /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("li", {
77
+ role: "option",
78
+ className: (0, __WEBPACK_EXTERNAL_MODULE_clsx__["default"])(__WEBPACK_EXTERNAL_MODULE__MenuButton_module_js_872b1a55__["default"].listItem, {
79
+ [__WEBPACK_EXTERNAL_MODULE__MenuButton_module_js_872b1a55__["default"].activeListItem]: activeIndex === index
80
+ }),
81
+ ...getItemProps({
82
+ ref: (node)=>{
83
+ optionsRef.current[index] = node;
84
+ }
85
+ }),
86
+ children: /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(NextLink, {
87
+ href: option.href,
88
+ children: option.label
89
+ })
90
+ }, option.id);
91
+ return /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("li", {
92
+ role: "option",
93
+ className: (0, __WEBPACK_EXTERNAL_MODULE_clsx__["default"])(__WEBPACK_EXTERNAL_MODULE__MenuButton_module_js_872b1a55__["default"].listItem, {
94
+ [__WEBPACK_EXTERNAL_MODULE__MenuButton_module_js_872b1a55__["default"].activeListItem]: activeIndex === index
95
+ }),
96
+ ...getItemProps({
97
+ onClick: "onClick" === option.type ? async (event)=>{
98
+ setIsOpen(false);
99
+ try {
100
+ setLoading(true);
101
+ await option.onClick(event);
102
+ } catch (error) {
103
+ if (error instanceof __WEBPACK_EXTERNAL_MODULE__forms_FormError_js_7d98929f__["default"]) setErrors(error.validationErrors);
104
+ else error instanceof Error ? setErrors([
105
+ {
106
+ id: (0, __WEBPACK_EXTERNAL_MODULE_uuid__.v4)(),
107
+ message: error.message
108
+ }
109
+ ]) : setErrors([
110
+ {
111
+ id: (0, __WEBPACK_EXTERNAL_MODULE_uuid__.v4)(),
112
+ message: "Unknown error."
113
+ }
114
+ ]);
115
+ }
116
+ setLoading(false);
117
+ } : ()=>{
118
+ setIsOpen(false);
119
+ setOptionToRender(option);
120
+ },
121
+ ref: (node)=>{
122
+ optionsRef.current[index] = node;
123
+ }
124
+ }),
125
+ children: option.label
126
+ }, option.id);
127
+ }, [
128
+ activeIndex,
129
+ getItemProps,
130
+ optionsRef,
131
+ NextLink
132
+ ]);
133
+ const renderOptions = (0, __WEBPACK_EXTERNAL_MODULE_react__.useCallback)(()=>{
134
+ if (options) return filteredOptions.map(renderOption);
135
+ return optionGroups.map((group)=>{
136
+ if (0 === group.options.length) return null;
137
+ const groupOptionIds = new Set(group.options.map((o)=>o?.id));
138
+ const filteredGroupOptions = filteredOptions.filter((o)=>groupOptionIds.has(o.id));
139
+ if (0 === filteredGroupOptions.length) return null;
140
+ const headerId = `${listboxId}-group-${group.header}`;
141
+ return /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsxs)("li", {
142
+ role: "presentation",
143
+ children: [
144
+ /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("div", {
145
+ id: headerId,
146
+ className: __WEBPACK_EXTERNAL_MODULE__MenuButton_module_js_872b1a55__["default"].listItemGroupHeader,
147
+ children: group.header
148
+ }),
149
+ /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("ul", {
150
+ role: "group",
151
+ "aria-labelledby": headerId,
152
+ children: filteredGroupOptions.map((option)=>{
153
+ const optionIndex = optionGroups.flatMap((group)=>group.options).indexOf(option);
154
+ return renderOption(option, optionIndex);
155
+ })
156
+ })
157
+ ]
158
+ }, group.header);
159
+ });
160
+ }, [
161
+ filteredOptions,
162
+ optionGroups,
163
+ options,
164
+ renderOption,
165
+ listboxId
166
+ ]);
74
167
  return /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsxs)(__WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.Fragment, {
75
168
  children: [
76
169
  /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsxs)("button", {
170
+ "aria-controls": listboxId,
171
+ "aria-haspopup": "listbox",
172
+ "aria-expanded": isOpen,
77
173
  autoFocus: autoFocus,
78
174
  disabled: disabled,
79
- className: __WEBPACK_EXTERNAL_MODULE__MenuButton_module_js_872b1a55__["default"].button,
175
+ className: (0, __WEBPACK_EXTERNAL_MODULE_clsx__["default"])(__WEBPACK_EXTERNAL_MODULE__MenuButton_module_js_872b1a55__["default"].button, {
176
+ [__WEBPACK_EXTERNAL_MODULE__MenuButton_module_js_872b1a55__["default"].fullWidthButton]: fullWidth
177
+ }),
80
178
  tabIndex: 0,
81
179
  type: "button",
82
180
  ref: floatingUiRefs.setReference,
83
181
  ...getReferenceProps({
84
- id: id
182
+ id: buttonId
85
183
  }),
86
184
  children: [
87
185
  loading ? /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE__presentation_Icon_js_6961aa57__["default"], {
@@ -98,90 +196,40 @@ const MenuButton = ({ autoFocus = false, disabled = false, id, options, searchPl
98
196
  }) : null
99
197
  ]
100
198
  }),
101
- !disabled && isOpen ? /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.Fragment, {
102
- children: /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE__floating_ui_react_3ddd630a__.FloatingPortal, {
103
- children: /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE__floating_ui_react_3ddd630a__.FloatingFocusManager, {
104
- context: context,
105
- initialFocus: searchRef,
106
- modal: false,
107
- children: /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsxs)("div", {
108
- ref: floatingUiRefs.setFloating,
109
- style: floatingStyles,
110
- className: (0, __WEBPACK_EXTERNAL_MODULE_clsx__["default"])(__WEBPACK_EXTERNAL_MODULE__MenuButton_module_js_872b1a55__["default"].menu),
111
- ...getFloatingProps(),
112
- children: [
113
- displaySearch ? /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("input", {
114
- type: "text",
115
- className: __WEBPACK_EXTERNAL_MODULE__MenuButton_module_js_872b1a55__["default"].searchInput,
116
- placeholder: searchPlaceholder ?? "Search for an option",
117
- "aria-autocomplete": "list",
118
- onChange: (e)=>{
119
- setSearchTerm(e.target.value);
120
- setActiveIndex(0);
121
- },
122
- value: searchTerm,
123
- ref: searchRef,
124
- onKeyDown: (event)=>{
125
- if ("Enter" === event.key && null != activeIndex) setIsOpen(false);
126
- }
127
- }) : null,
128
- 0 !== filteredOptions.length ? /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("ul", {
129
- className: __WEBPACK_EXTERNAL_MODULE__MenuButton_module_js_872b1a55__["default"].listBox,
130
- ref: listBoxRef,
131
- children: filteredOptions.map((option, index)=>{
132
- if ("link" === option.type) return /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("li", {
133
- className: (0, __WEBPACK_EXTERNAL_MODULE_clsx__["default"])(__WEBPACK_EXTERNAL_MODULE__MenuButton_module_js_872b1a55__["default"].listItem, {
134
- [__WEBPACK_EXTERNAL_MODULE__MenuButton_module_js_872b1a55__["default"].activeListItem]: activeIndex === index
135
- }),
136
- ...getItemProps({
137
- ref: (node)=>{
138
- optionsRef.current[index] = node;
139
- }
140
- }),
141
- children: /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(NextLink, {
142
- href: option.href,
143
- children: option.label
144
- })
145
- }, option.id);
146
- return /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("li", {
147
- className: (0, __WEBPACK_EXTERNAL_MODULE_clsx__["default"])(__WEBPACK_EXTERNAL_MODULE__MenuButton_module_js_872b1a55__["default"].listItem, {
148
- [__WEBPACK_EXTERNAL_MODULE__MenuButton_module_js_872b1a55__["default"].activeListItem]: activeIndex === index
149
- }),
150
- ...getItemProps({
151
- onClick: "onClick" === option.type ? async (event)=>{
152
- setIsOpen(false);
153
- try {
154
- setLoading(true);
155
- await option.onClick(event);
156
- } catch (error) {
157
- if (error instanceof __WEBPACK_EXTERNAL_MODULE__forms_FormError_js_7d98929f__["default"]) setErrors(error.validationErrors);
158
- else error instanceof Error ? setErrors([
159
- {
160
- id: (0, __WEBPACK_EXTERNAL_MODULE_uuid__.v4)(),
161
- message: error.message
162
- }
163
- ]) : setErrors([
164
- {
165
- id: (0, __WEBPACK_EXTERNAL_MODULE_uuid__.v4)(),
166
- message: "Unknown error."
167
- }
168
- ]);
169
- }
170
- setLoading(false);
171
- } : ()=>{
172
- setIsOpen(false);
173
- setOptionToRender(option);
174
- },
175
- ref: (node)=>{
176
- optionsRef.current[index] = node;
177
- }
178
- }),
179
- children: option.label
180
- }, option.id);
181
- })
182
- }) : null
183
- ]
184
- })
199
+ !disabled && isOpen ? /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE__floating_ui_react_3ddd630a__.FloatingPortal, {
200
+ children: /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)(__WEBPACK_EXTERNAL_MODULE__floating_ui_react_3ddd630a__.FloatingFocusManager, {
201
+ context: context,
202
+ initialFocus: searchRef,
203
+ modal: false,
204
+ children: /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsxs)("div", {
205
+ ref: floatingUiRefs.setFloating,
206
+ style: floatingStyles,
207
+ className: (0, __WEBPACK_EXTERNAL_MODULE_clsx__["default"])(__WEBPACK_EXTERNAL_MODULE__MenuButton_module_js_872b1a55__["default"].menu),
208
+ ...getFloatingProps(),
209
+ children: [
210
+ displaySearch ? /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("input", {
211
+ type: "text",
212
+ className: __WEBPACK_EXTERNAL_MODULE__MenuButton_module_js_872b1a55__["default"].searchInput,
213
+ placeholder: searchPlaceholder ?? "Search for an option",
214
+ "aria-autocomplete": "list",
215
+ onChange: (e)=>{
216
+ setSearchTerm(e.target.value);
217
+ setActiveIndex(0);
218
+ },
219
+ value: searchTerm,
220
+ ref: searchRef,
221
+ onKeyDown: (event)=>{
222
+ if ("Enter" === event.key && null != activeIndex) setIsOpen(false);
223
+ }
224
+ }) : null,
225
+ 0 !== filteredOptions.length ? /*#__PURE__*/ (0, __WEBPACK_EXTERNAL_MODULE_react_jsx_runtime_225474f2__.jsx)("ul", {
226
+ className: __WEBPACK_EXTERNAL_MODULE__MenuButton_module_js_872b1a55__["default"].listBox,
227
+ id: listboxId,
228
+ role: "listbox",
229
+ "aria-labelledby": buttonId,
230
+ children: renderOptions()
231
+ }) : null
232
+ ]
185
233
  })
186
234
  })
187
235
  }) : null,
@@ -1,9 +1,11 @@
1
1
  import "./MenuButton_module.css";
2
2
  const MenuButton_module_rslib_entry_ = {
3
3
  button: "button-HIHLjl",
4
+ fullWidthButton: "fullWidthButton-Q2zYOT",
4
5
  searchInput: "searchInput-bGdium",
5
6
  menu: "menu-jQY3kP",
6
7
  listBox: "listBox-IanUVy",
8
+ listItemGroupHeader: "listItemGroupHeader-ijzhOF",
7
9
  listItem: "listItem-PEQEDL",
8
10
  activeListItem: "activeListItem-iAlKFS"
9
11
  };
@@ -23,6 +23,10 @@
23
23
  background-color: var(--color-grey-t08);
24
24
  }
25
25
 
26
+ .fullWidthButton-Q2zYOT {
27
+ width: 100%;
28
+ }
29
+
26
30
  .button-HIHLjl:disabled {
27
31
  background: var(--color-grey-t09);
28
32
  border-color: var(--color-grey-t09);
@@ -59,6 +63,11 @@
59
63
  overflow-y: auto;
60
64
  }
61
65
 
66
+ .listItemGroupHeader-ijzhOF {
67
+ padding: var(--size-n2) var(--size-n2) var(--size-n2) 0;
68
+ font: var(--font-regular-bold);
69
+ }
70
+
62
71
  .listItem-PEQEDL {
63
72
  padding: var(--size-n2);
64
73
  border-radius: var(--size-n3);
@@ -20,10 +20,10 @@ const Tooltip = ({ children, content })=>{
20
20
  whileElementsMounted: __WEBPACK_EXTERNAL_MODULE__floating_ui_react_3ddd630a__.autoUpdate
21
21
  });
22
22
  const hover = (0, __WEBPACK_EXTERNAL_MODULE__floating_ui_react_3ddd630a__.useHover)(context, {
23
- move: false,
24
- restMs: 25,
23
+ move: true,
24
+ restMs: 50,
25
25
  delay: {
26
- open: 200,
26
+ open: 0,
27
27
  close: 0
28
28
  }
29
29
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@worknice/whiteboard",
3
3
  "description": "",
4
- "version": "0.12.10",
4
+ "version": "0.13.0",
5
5
  "license": "MIT",
6
6
  "private": false,
7
7
  "files": [
@@ -38,7 +38,7 @@
38
38
  "react-markdown": "^10.1.0",
39
39
  "utf8": "^3.0.0",
40
40
  "zod": "^3.22.3",
41
- "@worknice/utils": "^0.6.33"
41
+ "@worknice/utils": "^0.6.35"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@anolilab/semantic-release-pnpm": "^1.1.10",