@react-fabric/core 1.0.0-beta.39 → 1.0.0-beta.40

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 (36) hide show
  1. package/css/components/_panel.css +1 -1
  2. package/css/core/_content.css +1 -7
  3. package/css/reset/_theme.css +1 -0
  4. package/css/theme/_colors.css +2 -2
  5. package/css/theme/_utilities.css +6 -2
  6. package/dist/cjs/components/animations/Animations.js +2 -2
  7. package/dist/cjs/components/dropdown/DropdownTool.js +1 -1
  8. package/dist/cjs/components/media/Media.js +108 -71
  9. package/dist/cjs/components/menu/Menu.js +5 -5
  10. package/dist/cjs/components/tabpanel/Tab.js +1 -1
  11. package/dist/cjs/hotkeys/HotKeyLabel.js +1 -1
  12. package/dist/cjs/hotkeys/HotKeyWrapper.js +3 -1
  13. package/dist/cjs/overlays/alert/Alert.js +1 -1
  14. package/dist/cjs/overlays/flyout/Flyout.js +2 -1
  15. package/dist/cjs/overlays/modal/Modal.js +2 -1
  16. package/dist/esm/components/animations/Animations.js +2 -2
  17. package/dist/esm/components/dropdown/DropdownTool.js +1 -1
  18. package/dist/esm/components/media/Media.js +109 -72
  19. package/dist/esm/components/menu/Menu.js +5 -5
  20. package/dist/esm/components/tabpanel/Tab.js +1 -1
  21. package/dist/esm/hotkeys/HotKeyLabel.js +1 -1
  22. package/dist/esm/hotkeys/HotKeyWrapper.js +3 -1
  23. package/dist/esm/overlays/alert/Alert.js +1 -1
  24. package/dist/esm/overlays/flyout/Flyout.js +2 -1
  25. package/dist/esm/overlays/modal/Modal.js +2 -1
  26. package/package.json +3 -3
  27. package/src/components/animations/Animations.tsx +2 -2
  28. package/src/components/dropdown/DropdownTool.tsx +1 -1
  29. package/src/components/media/Media.tsx +127 -97
  30. package/src/components/menu/Menu.tsx +9 -9
  31. package/src/components/tabpanel/Tab.tsx +3 -1
  32. package/src/hotkeys/HotKeyLabel.tsx +1 -1
  33. package/src/hotkeys/HotKeyWrapper.tsx +3 -1
  34. package/src/overlays/alert/Alert.tsx +1 -1
  35. package/src/overlays/flyout/Flyout.tsx +2 -1
  36. package/src/overlays/modal/Modal.tsx +2 -1
@@ -24,7 +24,7 @@
24
24
  @layer components {
25
25
  .fabric-panel {
26
26
  grid-template: "head" auto "content" 1fr / 1fr;
27
- background-color: var(--color-bg);
27
+ background-color: var(--fabric-bg);
28
28
 
29
29
  & > * {
30
30
  background-color: transparent;
@@ -23,14 +23,8 @@
23
23
 
24
24
  @layer components {
25
25
  .fabric-content {
26
- --fabric-content: color-mix(
27
- in lab,
28
- var(--fabric-bg) 97%,
29
- var(--color-tint)
30
- );
31
-
32
26
  overflow: auto;
33
27
  padding: 1rem;
34
- background-color: var(--fabric-content);
28
+ background-color: var(--bg-color-content);
35
29
  }
36
30
  }
@@ -340,6 +340,7 @@
340
340
 
341
341
  /* bg */
342
342
  --bg-color-base: color-mix(in oklab, var(--fabric-bg) 99%, var(--color-tint));
343
+ --bg-color-content: color-mix(in lab, var(--fabric-bg) 97%, var(--color-tint));
343
344
  --bg-color-dimmed: color-mix(in oklab, var(--fabric-bg) 90%, var(--color-tint));
344
345
  --bg-color-muted: color-mix(in oklab, var(--fabric-bg) 80%, var(--color-tint));
345
346
  --bg-color-gray: color-mix(in oklab, var(--fabric-bg) 50%, var(--color-tint));
@@ -22,7 +22,7 @@
22
22
  */
23
23
 
24
24
  @theme static {
25
- --color-black: #232426;
25
+ --color-black: #121213;
26
26
  --color-white: #fffefd;
27
27
  --color-silver: #989da5;
28
28
  --color-steel: #7c99af;
@@ -317,4 +317,4 @@
317
317
  --color-wood-800: ;
318
318
  --color-wood-900: ;
319
319
  --color-wood-950: ;
320
- }
320
+ }
@@ -40,7 +40,7 @@
40
40
  @variant dark (&:where([data-color-scheme="dark"], [data-color-scheme="dark"] *));
41
41
 
42
42
  @utility area-* {
43
- grid-area: --value([ *]);
43
+ grid-area: --value([*]);
44
44
  }
45
45
 
46
46
  @utility area-content {
@@ -195,6 +195,10 @@
195
195
  background-color: var(--bg-color-base);
196
196
  }
197
197
 
198
+ @utility bg-content {
199
+ background-color: var(--bg-color-content);
200
+ }
201
+
198
202
  @utility bg-dimmed {
199
203
  background-color: var(--bg-color-dimmed);
200
204
  }
@@ -349,7 +353,7 @@
349
353
 
350
354
  @utility gap-* {
351
355
  &:where(.fabric-row) {
352
- --gutter: calc(var(--spacing) * --value(number));
356
+ --gutter: calc(var(--spacing) * --value(integer));
353
357
 
354
358
  gap: 0 !important;
355
359
  }
@@ -67,7 +67,7 @@ var AnimationBars = function AnimationBars(_ref2) {
67
67
  return /*#__PURE__*/jsxRuntime.jsx("div", {
68
68
  className: classNames("absolute inset-0 z-10 flex justify-center items-center cursor-wait"),
69
69
  children: /*#__PURE__*/jsxRuntime.jsx("div", {
70
- className: classNames("outline outline-tint-500/20 p-4 bg-base/50 backdrop-blur-sm rounded"),
70
+ className: classNames("outline outline-tint-500/20 p-4 bg-tint-50/50 backdrop-blur-sm rounded"),
71
71
  children: /*#__PURE__*/jsxRuntime.jsx("div", {
72
72
  className: classNames("fabric-bars"),
73
73
  style: {
@@ -87,7 +87,7 @@ var AnimationSpinner = function AnimationSpinner(_ref3) {
87
87
  className: classNames("absolute inset-0 z-10 flex justify-center items-center cursor-wait"),
88
88
  "data-ref": "spinner",
89
89
  children: /*#__PURE__*/jsxRuntime.jsx("div", {
90
- className: classNames("outline outline-tint-500/20 p-4 bg-base/50 backdrop-blur-sm rounded"),
90
+ className: classNames("outline outline-tint-500/20 p-4 bg-tint-50/50 backdrop-blur-sm rounded"),
91
91
  children: /*#__PURE__*/jsxRuntime.jsx("div", {
92
92
  className: classNames("fabric-spinner"),
93
93
  style: {
@@ -20,7 +20,7 @@ var DropdownTool = function DropdownTool(_ref) {
20
20
  return /*#__PURE__*/jsxRuntime.jsxs(Dropdown.Dropdown, _objectSpread(_objectSpread({}, props), {}, {
21
21
  children: [/*#__PURE__*/jsxRuntime.jsx(Icon.Icon, {
22
22
  icon: icon !== null && icon !== void 0 ? icon : icons.CoreIcons.caretDown,
23
- className: classNames(className, "outline bg-base/50 flex-content pointer-events-auto cursor-pointer", groupHover && "hidden group-hover/tool:inline-block data-[dropdown-open]:inline-block")
23
+ className: classNames("outline bg-tint-50/50 flex-content pointer-events-auto cursor-pointer", className, groupHover && "hidden group-hover/tool:inline-block data-[dropdown-open]:inline-block")
24
24
  }), children]
25
25
  }));
26
26
  };
@@ -8,6 +8,7 @@ var classNames = require('classnames');
8
8
  var react = require('react');
9
9
  var icons = require('../../types/icons.js');
10
10
  var Button = require('../button/Button.js');
11
+ var Icon = require('../icon/Icon.js');
11
12
  var jsxRuntime = require('react/jsx-runtime');
12
13
 
13
14
  var _excluded = ["src", "alt", "reel", "fit", "className"],
@@ -19,6 +20,10 @@ var cover = {
19
20
  cover: "object-cover",
20
21
  contain: "object-contain"
21
22
  };
23
+ var loadIndicator = /*#__PURE__*/jsxRuntime.jsx("div", {
24
+ className: classNames("fabric-mediaPlaceholder", "absolute z-1 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-4xl text-gray animate-pulse"),
25
+ children: "\u25CF\u25CF\u25CF"
26
+ });
22
27
  var Image = function Image(_ref) {
23
28
  var src = _ref.src,
24
29
  alt = _ref.alt,
@@ -31,28 +36,40 @@ var Image = function Image(_ref) {
31
36
  _useState2 = _slicedToArray(_useState, 2),
32
37
  colorSet = _useState2[0],
33
38
  setColorSet = _useState2[1];
34
- var loadIndicator = react.useMemo(function () {
35
- return /*#__PURE__*/jsxRuntime.jsx("div", {
36
- className: classNames("fabric-mediaPlaceholder", "absolute z-1 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-4xl text-gray animate-pulse"),
37
- children: "\u25CF\u25CF\u25CF"
38
- });
39
+ var _useState3 = react.useState(false),
40
+ _useState4 = _slicedToArray(_useState3, 2),
41
+ loaded = _useState4[0],
42
+ setLoaded = _useState4[1];
43
+ var _useState5 = react.useState(false),
44
+ _useState6 = _slicedToArray(_useState5, 2),
45
+ errored = _useState6[0],
46
+ setErrored = _useState6[1];
47
+ react.useEffect(function () {
48
+ setLoaded(false);
49
+ setErrored(false);
39
50
  }, [src]);
40
51
  return /*#__PURE__*/jsxRuntime.jsxs("div", _objectSpread(_objectSpread({
41
52
  "data-ref": "image",
42
53
  className: classNames("fabric-mediaContainer", "relative overflow-hidden inline-block", reel && "fabric-mediaReel", className),
43
54
  "data-colorset": colorSet
44
55
  }, aria), {}, {
45
- children: [loadIndicator, /*#__PURE__*/jsxRuntime.jsx("img", {
56
+ children: [!loaded && !errored && loadIndicator, !errored && /*#__PURE__*/jsxRuntime.jsx("img", {
46
57
  alt: alt !== null && alt !== void 0 ? alt : src,
47
58
  src: src,
48
59
  loading: "lazy",
49
60
  crossOrigin: "anonymous",
50
61
  className: classNames("fabric-media", "pointer-events-none w-full h-full", cover[fit]),
51
62
  onLoad: function onLoad(e) {
52
- var _e$currentTarget$prev;
53
- (_e$currentTarget$prev = e.currentTarget.previousElementSibling) === null || _e$currentTarget$prev === void 0 || _e$currentTarget$prev.remove();
63
+ setLoaded(true);
54
64
  setColorSet(utilities.getImageColorset(e.currentTarget));
65
+ },
66
+ onError: function onError() {
67
+ return setErrored(true);
55
68
  }
69
+ }), errored && /*#__PURE__*/jsxRuntime.jsx(Icon.Icon, {
70
+ size: "2rem",
71
+ className: "absolute text-tint-300 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2",
72
+ icon: icons.CoreIcons.mediaImageBroken
56
73
  })]
57
74
  }));
58
75
  };
@@ -67,14 +84,29 @@ var Video = function Video(_ref2) {
67
84
  className = _ref2.className,
68
85
  aria = _objectWithoutProperties(_ref2, _excluded2);
69
86
  var videoRef = react.useRef(null);
70
- var _useState3 = react.useState(true),
71
- _useState4 = _slicedToArray(_useState3, 2),
72
- state = _useState4[0],
73
- setState = _useState4[1];
74
- var _useState5 = react.useState(false),
75
- _useState6 = _slicedToArray(_useState5, 2),
76
- volume = _useState6[0],
77
- setVolume = _useState6[1];
87
+ var _useState7 = react.useState(false),
88
+ _useState8 = _slicedToArray(_useState7, 2),
89
+ loaded = _useState8[0],
90
+ setLoaded = _useState8[1];
91
+ var _useState9 = react.useState(false),
92
+ _useState10 = _slicedToArray(_useState9, 2),
93
+ errored = _useState10[0],
94
+ setErrored = _useState10[1];
95
+ react.useEffect(function () {
96
+ setLoaded(false);
97
+ setErrored(false);
98
+ }, [src]);
99
+ var _useState11 = react.useState(true),
100
+ _useState12 = _slicedToArray(_useState11, 2),
101
+ state = _useState12[0],
102
+ setState = _useState12[1];
103
+ var _useState13 = react.useState(false),
104
+ _useState14 = _slicedToArray(_useState13, 2),
105
+ volume = _useState14[0],
106
+ setVolume = _useState14[1];
107
+ react.useEffect(function () {
108
+ setErrored(false);
109
+ }, [src]);
78
110
  return /*#__PURE__*/jsxRuntime.jsxs("div", _objectSpread(_objectSpread({
79
111
  "data-ref": "video",
80
112
  className: classNames("fabric-mediaContainer", "relative overflow-hidden inline-block", reel && "fabric-mediaReel", className),
@@ -87,64 +119,69 @@ var Video = function Video(_ref2) {
87
119
  playOnHover && ((_videoRef$current2 = videoRef.current) === null || _videoRef$current2 === void 0 ? void 0 : _videoRef$current2.pause());
88
120
  }
89
121
  }, aria), {}, {
90
- children: [/*#__PURE__*/jsxRuntime.jsx("div", {
91
- className: classNames("fabric-mediaPlaceholder", "absolute z-1 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-5xl animate-pulse"),
92
- children: "\u25CF\u25CF\u25CF"
93
- }), /*#__PURE__*/jsxRuntime.jsxs("video", {
94
- loop: true,
95
- ref: videoRef,
96
- controls: false,
97
- poster: poster,
98
- autoPlay: autoPlay,
99
- className: classNames("fabric-media", "pointer-events-none w-full h-full", fit && "object-".concat(fit)),
100
- onPlay: function onPlay() {
101
- return setState(true);
102
- },
103
- onPause: function onPause() {
104
- return setState(false);
105
- },
106
- onVolumeChange: function onVolumeChange(e) {
107
- return setVolume(!!e.currentTarget.volume);
108
- },
109
- onLoadedData: function onLoadedData(e) {
110
- var _e$currentTarget$prev2;
111
- e.currentTarget.volume = 0;
112
- (_e$currentTarget$prev2 = e.currentTarget.previousElementSibling) === null || _e$currentTarget$prev2 === void 0 || _e$currentTarget$prev2.remove();
113
- },
114
- children: [/*#__PURE__*/jsxRuntime.jsx("source", {
115
- src: src
116
- }), /*#__PURE__*/jsxRuntime.jsx("track", {
117
- kind: "captions"
118
- })]
119
- }), /*#__PURE__*/jsxRuntime.jsxs("div", {
120
- "data-inner-clickable": true,
121
- className: classNames("fabric-mediaControls", "absolute bottom-6 end-6 rounded-full flex flex-col"),
122
- children: [/*#__PURE__*/jsxRuntime.jsx(Button.Button, {
123
- rounded: true,
124
- variant: "link",
125
- className: "fabric-mediaControlPlaceholder",
126
- icon: icons.CoreIcons.ellipsis,
127
- "aria-label": "controls"
128
- }), /*#__PURE__*/jsxRuntime.jsx(Button.Button, {
129
- rounded: true,
130
- variant: "link",
131
- "aria-label": "controls",
132
- className: "fabric-mediaControlAction",
133
- onClick: function onClick() {
134
- videoRef.current && (videoRef.current.volume = videoRef.current.volume ? 0 : 1);
122
+ children: [!loaded && !errored && loadIndicator, !errored && /*#__PURE__*/jsxRuntime.jsxs(react.Fragment, {
123
+ children: [/*#__PURE__*/jsxRuntime.jsxs("video", {
124
+ loop: true,
125
+ ref: videoRef,
126
+ controls: false,
127
+ poster: poster,
128
+ autoPlay: autoPlay,
129
+ className: classNames("fabric-media", "pointer-events-none w-full h-full", fit && "object-".concat(fit)),
130
+ onPlay: function onPlay() {
131
+ return setState(true);
132
+ },
133
+ onPause: function onPause() {
134
+ return setState(false);
135
+ },
136
+ onVolumeChange: function onVolumeChange(e) {
137
+ return setVolume(!!e.currentTarget.volume);
138
+ },
139
+ onLoadedData: function onLoadedData(e) {
140
+ e.currentTarget.volume = 0;
141
+ setLoaded(true);
135
142
  },
136
- icon: volume ? icons.CoreIcons.volumeOn : icons.CoreIcons.volumeOff
137
- }), /*#__PURE__*/jsxRuntime.jsx(Button.Button, {
138
- rounded: true,
139
- variant: "link",
140
- "aria-label": "controls",
141
- className: "fabric-mediaControlAction",
142
- onClick: function onClick() {
143
- var _videoRef$current3, _videoRef$current4, _videoRef$current5;
144
- (_videoRef$current3 = videoRef.current) !== null && _videoRef$current3 !== void 0 && _videoRef$current3.paused ? (_videoRef$current4 = videoRef.current) === null || _videoRef$current4 === void 0 ? void 0 : _videoRef$current4.play() : (_videoRef$current5 = videoRef.current) === null || _videoRef$current5 === void 0 ? void 0 : _videoRef$current5.pause();
143
+ onError: function onError() {
144
+ return setErrored(true);
145
145
  },
146
- icon: state ? icons.CoreIcons.pause : icons.CoreIcons.play
146
+ children: [/*#__PURE__*/jsxRuntime.jsx("source", {
147
+ src: src
148
+ }), /*#__PURE__*/jsxRuntime.jsx("track", {
149
+ kind: "captions"
150
+ })]
151
+ }), /*#__PURE__*/jsxRuntime.jsxs("div", {
152
+ "data-inner-clickable": true,
153
+ className: classNames("fabric-mediaControls", "absolute bottom-6 end-6 rounded-full flex flex-col"),
154
+ children: [/*#__PURE__*/jsxRuntime.jsx(Button.Button, {
155
+ rounded: true,
156
+ variant: "link",
157
+ className: "fabric-mediaControlPlaceholder",
158
+ icon: icons.CoreIcons.ellipsis,
159
+ "aria-label": "controls"
160
+ }), /*#__PURE__*/jsxRuntime.jsx(Button.Button, {
161
+ rounded: true,
162
+ variant: "link",
163
+ "aria-label": "controls",
164
+ className: "fabric-mediaControlAction",
165
+ onClick: function onClick() {
166
+ videoRef.current && (videoRef.current.volume = videoRef.current.volume ? 0 : 1);
167
+ },
168
+ icon: volume ? icons.CoreIcons.volumeOn : icons.CoreIcons.volumeOff
169
+ }), /*#__PURE__*/jsxRuntime.jsx(Button.Button, {
170
+ rounded: true,
171
+ variant: "link",
172
+ "aria-label": "controls",
173
+ className: "fabric-mediaControlAction",
174
+ onClick: function onClick() {
175
+ var _videoRef$current3, _videoRef$current4, _videoRef$current5;
176
+ (_videoRef$current3 = videoRef.current) !== null && _videoRef$current3 !== void 0 && _videoRef$current3.paused ? (_videoRef$current4 = videoRef.current) === null || _videoRef$current4 === void 0 ? void 0 : _videoRef$current4.play() : (_videoRef$current5 = videoRef.current) === null || _videoRef$current5 === void 0 ? void 0 : _videoRef$current5.pause();
177
+ },
178
+ icon: state ? icons.CoreIcons.pause : icons.CoreIcons.play
179
+ })]
147
180
  })]
181
+ }), errored && /*#__PURE__*/jsxRuntime.jsx(Icon.Icon, {
182
+ size: "2rem",
183
+ className: "absolute text-tint-300 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2",
184
+ icon: icons.CoreIcons.mediaVideoBroken
148
185
  })]
149
186
  }));
150
187
  };
@@ -205,6 +205,11 @@ var MenuComponent = function MenuComponent(_ref) {
205
205
  var Menu = function Menu(props) {
206
206
  var parentId = react.useFloatingParentNodeId();
207
207
  var tree = react.useFloatingTree();
208
+ if (tree) {
209
+ return /*#__PURE__*/jsxRuntime.jsx(MenuComponent, _objectSpread(_objectSpread({}, props), {}, {
210
+ menuClassName: props.className
211
+ }));
212
+ }
208
213
  if (parentId === null) {
209
214
  return /*#__PURE__*/jsxRuntime.jsx(react.FloatingTree, {
210
215
  children: /*#__PURE__*/jsxRuntime.jsx(MenuComponent, _objectSpread(_objectSpread({}, props), {}, {
@@ -212,11 +217,6 @@ var Menu = function Menu(props) {
212
217
  }))
213
218
  });
214
219
  }
215
- if (tree) {
216
- return /*#__PURE__*/jsxRuntime.jsx(MenuComponent, _objectSpread(_objectSpread({}, props), {}, {
217
- menuClassName: props.className
218
- }));
219
- }
220
220
  return /*#__PURE__*/jsxRuntime.jsx(MenuComponent, _objectSpread({}, props));
221
221
  };
222
222
 
@@ -80,7 +80,7 @@ var Tab = function Tab(_ref) {
80
80
  placement: minimal ? "end" : "",
81
81
  className: classNames("me-1", badgeProps.className)
82
82
  })), actions && active && /*#__PURE__*/jsxRuntime.jsx(DropdownTool.DropdownTool, {
83
- className: "bg-base/20 me-1 z-5",
83
+ className: "bg-tint-50/20 me-1 z-5",
84
84
  children: actions
85
85
  }), onClose && /*#__PURE__*/jsxRuntime.jsx("span", {
86
86
  role: "none",
@@ -44,7 +44,7 @@ var HotKeyLabel = function HotKeyLabel(_ref) {
44
44
  }, "label")];
45
45
  }, [keyCombo], "HotKey label");
46
46
  return /*#__PURE__*/jsxRuntime.jsx("kbd", {
47
- className: "hotkey-label inline-flex gap-1 select-none self-center font-mono items-center text-[0.625rem] leading-4 bg-tint-500/10 text-base/50 whitespace-nowrap rounded px-2 mx-1",
47
+ className: "hotkey-label inline-flex gap-1 select-none self-center font-mono items-center text-[0.625rem] leading-4 bg-tint-500/20 text-tint-900/65 whitespace-nowrap rounded px-2 mx-1",
48
48
  children: label
49
49
  });
50
50
  };
@@ -46,7 +46,9 @@ var HotKeyWrapper = /*#__PURE__*/react.memo(function (_ref) {
46
46
  ctrl = _ref4.ctrl,
47
47
  meta = _ref4.meta,
48
48
  shift = _ref4.shift;
49
- return (key.toLowerCase() === keyCode.toLowerCase() || key.toLowerCase() === code.toLowerCase()) && alt === altKey && ctrl === ctrlKey && meta === metaKey && shift === shiftKey;
49
+ return (key.toLowerCase() === keyCode.toLowerCase() || key.toLowerCase() === code.toLowerCase() ||
50
+ // work around for macos alt+key issue
51
+ "key".concat(key.toLowerCase()) === code.toLowerCase()) && alt === altKey && ctrl === ctrlKey && meta === metaKey && shift === shiftKey;
50
52
  });
51
53
  if (find !== null && find !== void 0 && find.handler) {
52
54
  find.handler();
@@ -101,7 +101,7 @@ var Alert = function Alert(_ref) {
101
101
  style: {
102
102
  zIndex: "var(--z-overlay-mask)"
103
103
  },
104
- className: "bg-base/50 backdrop-blur-sm grid place-items-center",
104
+ className: "bg-tint-100/50 backdrop-blur-sm grid place-items-center",
105
105
  children: /*#__PURE__*/jsxRuntime.jsx(react$1.FloatingFocusManager, {
106
106
  context: context,
107
107
  children: /*#__PURE__*/jsxRuntime.jsxs("dialog", _objectSpread(_objectSpread({
@@ -94,7 +94,7 @@ var Flyout = function Flyout(_ref) {
94
94
  style: {
95
95
  zIndex: "var(--z-overlay-mask)"
96
96
  },
97
- className: "bg-base/10 backdrop-blur-sm transition-all",
97
+ className: "bg-tint-100/10 backdrop-blur-sm transition-all",
98
98
  children: children
99
99
  });
100
100
  }, [hideMask, handleClose]);
@@ -115,6 +115,7 @@ var Flyout = function Flyout(_ref) {
115
115
  children: /*#__PURE__*/jsxRuntime.jsxs(HotKeyWrapper.HotKeyWrapper, {
116
116
  children: [/*#__PURE__*/jsxRuntime.jsxs(HeadFoot.Header, {
117
117
  flex: true,
118
+ align: "center",
118
119
  className: classNames("fabric-flyoutHeader", headerClassName),
119
120
  children: [utilities.isString(icon) && /*#__PURE__*/jsxRuntime.jsx(Icon.Icon, {
120
121
  icon: icon,
@@ -80,7 +80,7 @@ var Modal = function Modal(_ref) {
80
80
  style: {
81
81
  zIndex: "var(--z-overlay-mask)"
82
82
  },
83
- className: "bg-base/20 backdrop-blur-sm grid place-items-center",
83
+ className: "bg-tint-100/20 backdrop-blur-sm grid place-items-center",
84
84
  children: /*#__PURE__*/jsxRuntime.jsx(react.FloatingFocusManager, {
85
85
  context: context,
86
86
  children: /*#__PURE__*/jsxRuntime.jsx("dialog", _objectSpread(_objectSpread(_objectSpread({
@@ -96,6 +96,7 @@ var Modal = function Modal(_ref) {
96
96
  children: /*#__PURE__*/jsxRuntime.jsxs(HotKeyWrapper.HotKeyWrapper, {
97
97
  children: [/*#__PURE__*/jsxRuntime.jsxs(HeadFoot.Header, {
98
98
  flex: true,
99
+ align: "center",
99
100
  className: classNames("fabric-modalHeader", headerClassName),
100
101
  children: [utilities.isString(icon) && /*#__PURE__*/jsxRuntime.jsx(Icon.Icon, {
101
102
  icon: icon,
@@ -67,7 +67,7 @@ var AnimationBars = function AnimationBars(_ref2) {
67
67
  return /*#__PURE__*/jsx("div", {
68
68
  className: classNames("absolute inset-0 z-10 flex justify-center items-center cursor-wait"),
69
69
  children: /*#__PURE__*/jsx("div", {
70
- className: classNames("outline outline-tint-500/20 p-4 bg-base/50 backdrop-blur-sm rounded"),
70
+ className: classNames("outline outline-tint-500/20 p-4 bg-tint-50/50 backdrop-blur-sm rounded"),
71
71
  children: /*#__PURE__*/jsx("div", {
72
72
  className: classNames("fabric-bars"),
73
73
  style: {
@@ -87,7 +87,7 @@ var AnimationSpinner = function AnimationSpinner(_ref3) {
87
87
  className: classNames("absolute inset-0 z-10 flex justify-center items-center cursor-wait"),
88
88
  "data-ref": "spinner",
89
89
  children: /*#__PURE__*/jsx("div", {
90
- className: classNames("outline outline-tint-500/20 p-4 bg-base/50 backdrop-blur-sm rounded"),
90
+ className: classNames("outline outline-tint-500/20 p-4 bg-tint-50/50 backdrop-blur-sm rounded"),
91
91
  children: /*#__PURE__*/jsx("div", {
92
92
  className: classNames("fabric-spinner"),
93
93
  style: {
@@ -20,7 +20,7 @@ var DropdownTool = function DropdownTool(_ref) {
20
20
  return /*#__PURE__*/jsxs(Dropdown, _objectSpread(_objectSpread({}, props), {}, {
21
21
  children: [/*#__PURE__*/jsx(Icon, {
22
22
  icon: icon !== null && icon !== void 0 ? icon : CoreIcons.caretDown,
23
- className: classNames(className, "outline bg-base/50 flex-content pointer-events-auto cursor-pointer", groupHover && "hidden group-hover/tool:inline-block data-[dropdown-open]:inline-block")
23
+ className: classNames("outline bg-tint-50/50 flex-content pointer-events-auto cursor-pointer", className, groupHover && "hidden group-hover/tool:inline-block data-[dropdown-open]:inline-block")
24
24
  }), children]
25
25
  }));
26
26
  };
@@ -5,9 +5,10 @@ import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
5
5
  import _objectWithoutProperties from '@babel/runtime/helpers/objectWithoutProperties';
6
6
  import { getImageColorset } from '@react-fabric/utilities';
7
7
  import classNames from 'classnames';
8
- import { useState, useMemo, useRef } from 'react';
8
+ import { useState, useEffect, useRef, Fragment } from 'react';
9
9
  import { CoreIcons } from '../../types/icons.js';
10
10
  import { Button } from '../button/Button.js';
11
+ import { Icon } from '../icon/Icon.js';
11
12
  import { jsx, jsxs } from 'react/jsx-runtime';
12
13
 
13
14
  var _excluded = ["src", "alt", "reel", "fit", "className"],
@@ -19,6 +20,10 @@ var cover = {
19
20
  cover: "object-cover",
20
21
  contain: "object-contain"
21
22
  };
23
+ var loadIndicator = /*#__PURE__*/jsx("div", {
24
+ className: classNames("fabric-mediaPlaceholder", "absolute z-1 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-4xl text-gray animate-pulse"),
25
+ children: "\u25CF\u25CF\u25CF"
26
+ });
22
27
  var Image = function Image(_ref) {
23
28
  var src = _ref.src,
24
29
  alt = _ref.alt,
@@ -31,28 +36,40 @@ var Image = function Image(_ref) {
31
36
  _useState2 = _slicedToArray(_useState, 2),
32
37
  colorSet = _useState2[0],
33
38
  setColorSet = _useState2[1];
34
- var loadIndicator = useMemo(function () {
35
- return /*#__PURE__*/jsx("div", {
36
- className: classNames("fabric-mediaPlaceholder", "absolute z-1 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-4xl text-gray animate-pulse"),
37
- children: "\u25CF\u25CF\u25CF"
38
- });
39
+ var _useState3 = useState(false),
40
+ _useState4 = _slicedToArray(_useState3, 2),
41
+ loaded = _useState4[0],
42
+ setLoaded = _useState4[1];
43
+ var _useState5 = useState(false),
44
+ _useState6 = _slicedToArray(_useState5, 2),
45
+ errored = _useState6[0],
46
+ setErrored = _useState6[1];
47
+ useEffect(function () {
48
+ setLoaded(false);
49
+ setErrored(false);
39
50
  }, [src]);
40
51
  return /*#__PURE__*/jsxs("div", _objectSpread(_objectSpread({
41
52
  "data-ref": "image",
42
53
  className: classNames("fabric-mediaContainer", "relative overflow-hidden inline-block", reel && "fabric-mediaReel", className),
43
54
  "data-colorset": colorSet
44
55
  }, aria), {}, {
45
- children: [loadIndicator, /*#__PURE__*/jsx("img", {
56
+ children: [!loaded && !errored && loadIndicator, !errored && /*#__PURE__*/jsx("img", {
46
57
  alt: alt !== null && alt !== void 0 ? alt : src,
47
58
  src: src,
48
59
  loading: "lazy",
49
60
  crossOrigin: "anonymous",
50
61
  className: classNames("fabric-media", "pointer-events-none w-full h-full", cover[fit]),
51
62
  onLoad: function onLoad(e) {
52
- var _e$currentTarget$prev;
53
- (_e$currentTarget$prev = e.currentTarget.previousElementSibling) === null || _e$currentTarget$prev === void 0 || _e$currentTarget$prev.remove();
63
+ setLoaded(true);
54
64
  setColorSet(getImageColorset(e.currentTarget));
65
+ },
66
+ onError: function onError() {
67
+ return setErrored(true);
55
68
  }
69
+ }), errored && /*#__PURE__*/jsx(Icon, {
70
+ size: "2rem",
71
+ className: "absolute text-tint-300 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2",
72
+ icon: CoreIcons.mediaImageBroken
56
73
  })]
57
74
  }));
58
75
  };
@@ -67,14 +84,29 @@ var Video = function Video(_ref2) {
67
84
  className = _ref2.className,
68
85
  aria = _objectWithoutProperties(_ref2, _excluded2);
69
86
  var videoRef = useRef(null);
70
- var _useState3 = useState(true),
71
- _useState4 = _slicedToArray(_useState3, 2),
72
- state = _useState4[0],
73
- setState = _useState4[1];
74
- var _useState5 = useState(false),
75
- _useState6 = _slicedToArray(_useState5, 2),
76
- volume = _useState6[0],
77
- setVolume = _useState6[1];
87
+ var _useState7 = useState(false),
88
+ _useState8 = _slicedToArray(_useState7, 2),
89
+ loaded = _useState8[0],
90
+ setLoaded = _useState8[1];
91
+ var _useState9 = useState(false),
92
+ _useState10 = _slicedToArray(_useState9, 2),
93
+ errored = _useState10[0],
94
+ setErrored = _useState10[1];
95
+ useEffect(function () {
96
+ setLoaded(false);
97
+ setErrored(false);
98
+ }, [src]);
99
+ var _useState11 = useState(true),
100
+ _useState12 = _slicedToArray(_useState11, 2),
101
+ state = _useState12[0],
102
+ setState = _useState12[1];
103
+ var _useState13 = useState(false),
104
+ _useState14 = _slicedToArray(_useState13, 2),
105
+ volume = _useState14[0],
106
+ setVolume = _useState14[1];
107
+ useEffect(function () {
108
+ setErrored(false);
109
+ }, [src]);
78
110
  return /*#__PURE__*/jsxs("div", _objectSpread(_objectSpread({
79
111
  "data-ref": "video",
80
112
  className: classNames("fabric-mediaContainer", "relative overflow-hidden inline-block", reel && "fabric-mediaReel", className),
@@ -87,64 +119,69 @@ var Video = function Video(_ref2) {
87
119
  playOnHover && ((_videoRef$current2 = videoRef.current) === null || _videoRef$current2 === void 0 ? void 0 : _videoRef$current2.pause());
88
120
  }
89
121
  }, aria), {}, {
90
- children: [/*#__PURE__*/jsx("div", {
91
- className: classNames("fabric-mediaPlaceholder", "absolute z-1 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-5xl animate-pulse"),
92
- children: "\u25CF\u25CF\u25CF"
93
- }), /*#__PURE__*/jsxs("video", {
94
- loop: true,
95
- ref: videoRef,
96
- controls: false,
97
- poster: poster,
98
- autoPlay: autoPlay,
99
- className: classNames("fabric-media", "pointer-events-none w-full h-full", fit && "object-".concat(fit)),
100
- onPlay: function onPlay() {
101
- return setState(true);
102
- },
103
- onPause: function onPause() {
104
- return setState(false);
105
- },
106
- onVolumeChange: function onVolumeChange(e) {
107
- return setVolume(!!e.currentTarget.volume);
108
- },
109
- onLoadedData: function onLoadedData(e) {
110
- var _e$currentTarget$prev2;
111
- e.currentTarget.volume = 0;
112
- (_e$currentTarget$prev2 = e.currentTarget.previousElementSibling) === null || _e$currentTarget$prev2 === void 0 || _e$currentTarget$prev2.remove();
113
- },
114
- children: [/*#__PURE__*/jsx("source", {
115
- src: src
116
- }), /*#__PURE__*/jsx("track", {
117
- kind: "captions"
118
- })]
119
- }), /*#__PURE__*/jsxs("div", {
120
- "data-inner-clickable": true,
121
- className: classNames("fabric-mediaControls", "absolute bottom-6 end-6 rounded-full flex flex-col"),
122
- children: [/*#__PURE__*/jsx(Button, {
123
- rounded: true,
124
- variant: "link",
125
- className: "fabric-mediaControlPlaceholder",
126
- icon: CoreIcons.ellipsis,
127
- "aria-label": "controls"
128
- }), /*#__PURE__*/jsx(Button, {
129
- rounded: true,
130
- variant: "link",
131
- "aria-label": "controls",
132
- className: "fabric-mediaControlAction",
133
- onClick: function onClick() {
134
- videoRef.current && (videoRef.current.volume = videoRef.current.volume ? 0 : 1);
122
+ children: [!loaded && !errored && loadIndicator, !errored && /*#__PURE__*/jsxs(Fragment, {
123
+ children: [/*#__PURE__*/jsxs("video", {
124
+ loop: true,
125
+ ref: videoRef,
126
+ controls: false,
127
+ poster: poster,
128
+ autoPlay: autoPlay,
129
+ className: classNames("fabric-media", "pointer-events-none w-full h-full", fit && "object-".concat(fit)),
130
+ onPlay: function onPlay() {
131
+ return setState(true);
132
+ },
133
+ onPause: function onPause() {
134
+ return setState(false);
135
+ },
136
+ onVolumeChange: function onVolumeChange(e) {
137
+ return setVolume(!!e.currentTarget.volume);
138
+ },
139
+ onLoadedData: function onLoadedData(e) {
140
+ e.currentTarget.volume = 0;
141
+ setLoaded(true);
135
142
  },
136
- icon: volume ? CoreIcons.volumeOn : CoreIcons.volumeOff
137
- }), /*#__PURE__*/jsx(Button, {
138
- rounded: true,
139
- variant: "link",
140
- "aria-label": "controls",
141
- className: "fabric-mediaControlAction",
142
- onClick: function onClick() {
143
- var _videoRef$current3, _videoRef$current4, _videoRef$current5;
144
- (_videoRef$current3 = videoRef.current) !== null && _videoRef$current3 !== void 0 && _videoRef$current3.paused ? (_videoRef$current4 = videoRef.current) === null || _videoRef$current4 === void 0 ? void 0 : _videoRef$current4.play() : (_videoRef$current5 = videoRef.current) === null || _videoRef$current5 === void 0 ? void 0 : _videoRef$current5.pause();
143
+ onError: function onError() {
144
+ return setErrored(true);
145
145
  },
146
- icon: state ? CoreIcons.pause : CoreIcons.play
146
+ children: [/*#__PURE__*/jsx("source", {
147
+ src: src
148
+ }), /*#__PURE__*/jsx("track", {
149
+ kind: "captions"
150
+ })]
151
+ }), /*#__PURE__*/jsxs("div", {
152
+ "data-inner-clickable": true,
153
+ className: classNames("fabric-mediaControls", "absolute bottom-6 end-6 rounded-full flex flex-col"),
154
+ children: [/*#__PURE__*/jsx(Button, {
155
+ rounded: true,
156
+ variant: "link",
157
+ className: "fabric-mediaControlPlaceholder",
158
+ icon: CoreIcons.ellipsis,
159
+ "aria-label": "controls"
160
+ }), /*#__PURE__*/jsx(Button, {
161
+ rounded: true,
162
+ variant: "link",
163
+ "aria-label": "controls",
164
+ className: "fabric-mediaControlAction",
165
+ onClick: function onClick() {
166
+ videoRef.current && (videoRef.current.volume = videoRef.current.volume ? 0 : 1);
167
+ },
168
+ icon: volume ? CoreIcons.volumeOn : CoreIcons.volumeOff
169
+ }), /*#__PURE__*/jsx(Button, {
170
+ rounded: true,
171
+ variant: "link",
172
+ "aria-label": "controls",
173
+ className: "fabric-mediaControlAction",
174
+ onClick: function onClick() {
175
+ var _videoRef$current3, _videoRef$current4, _videoRef$current5;
176
+ (_videoRef$current3 = videoRef.current) !== null && _videoRef$current3 !== void 0 && _videoRef$current3.paused ? (_videoRef$current4 = videoRef.current) === null || _videoRef$current4 === void 0 ? void 0 : _videoRef$current4.play() : (_videoRef$current5 = videoRef.current) === null || _videoRef$current5 === void 0 ? void 0 : _videoRef$current5.pause();
177
+ },
178
+ icon: state ? CoreIcons.pause : CoreIcons.play
179
+ })]
147
180
  })]
181
+ }), errored && /*#__PURE__*/jsx(Icon, {
182
+ size: "2rem",
183
+ className: "absolute text-tint-300 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2",
184
+ icon: CoreIcons.mediaVideoBroken
148
185
  })]
149
186
  }));
150
187
  };
@@ -205,6 +205,11 @@ var MenuComponent = function MenuComponent(_ref) {
205
205
  var Menu = function Menu(props) {
206
206
  var parentId = useFloatingParentNodeId();
207
207
  var tree = useFloatingTree();
208
+ if (tree) {
209
+ return /*#__PURE__*/jsx(MenuComponent, _objectSpread(_objectSpread({}, props), {}, {
210
+ menuClassName: props.className
211
+ }));
212
+ }
208
213
  if (parentId === null) {
209
214
  return /*#__PURE__*/jsx(FloatingTree, {
210
215
  children: /*#__PURE__*/jsx(MenuComponent, _objectSpread(_objectSpread({}, props), {}, {
@@ -212,11 +217,6 @@ var Menu = function Menu(props) {
212
217
  }))
213
218
  });
214
219
  }
215
- if (tree) {
216
- return /*#__PURE__*/jsx(MenuComponent, _objectSpread(_objectSpread({}, props), {}, {
217
- menuClassName: props.className
218
- }));
219
- }
220
220
  return /*#__PURE__*/jsx(MenuComponent, _objectSpread({}, props));
221
221
  };
222
222
 
@@ -80,7 +80,7 @@ var Tab = function Tab(_ref) {
80
80
  placement: minimal ? "end" : "",
81
81
  className: classNames("me-1", badgeProps.className)
82
82
  })), actions && active && /*#__PURE__*/jsx(DropdownTool, {
83
- className: "bg-base/20 me-1 z-5",
83
+ className: "bg-tint-50/20 me-1 z-5",
84
84
  children: actions
85
85
  }), onClose && /*#__PURE__*/jsx("span", {
86
86
  role: "none",
@@ -44,7 +44,7 @@ var HotKeyLabel = function HotKeyLabel(_ref) {
44
44
  }, "label")];
45
45
  }, [keyCombo], "HotKey label");
46
46
  return /*#__PURE__*/jsx("kbd", {
47
- className: "hotkey-label inline-flex gap-1 select-none self-center font-mono items-center text-[0.625rem] leading-4 bg-tint-500/10 text-base/50 whitespace-nowrap rounded px-2 mx-1",
47
+ className: "hotkey-label inline-flex gap-1 select-none self-center font-mono items-center text-[0.625rem] leading-4 bg-tint-500/20 text-tint-900/65 whitespace-nowrap rounded px-2 mx-1",
48
48
  children: label
49
49
  });
50
50
  };
@@ -46,7 +46,9 @@ var HotKeyWrapper = /*#__PURE__*/memo(function (_ref) {
46
46
  ctrl = _ref4.ctrl,
47
47
  meta = _ref4.meta,
48
48
  shift = _ref4.shift;
49
- return (key.toLowerCase() === keyCode.toLowerCase() || key.toLowerCase() === code.toLowerCase()) && alt === altKey && ctrl === ctrlKey && meta === metaKey && shift === shiftKey;
49
+ return (key.toLowerCase() === keyCode.toLowerCase() || key.toLowerCase() === code.toLowerCase() ||
50
+ // work around for macos alt+key issue
51
+ "key".concat(key.toLowerCase()) === code.toLowerCase()) && alt === altKey && ctrl === ctrlKey && meta === metaKey && shift === shiftKey;
50
52
  });
51
53
  if (find !== null && find !== void 0 && find.handler) {
52
54
  find.handler();
@@ -101,7 +101,7 @@ var Alert = function Alert(_ref) {
101
101
  style: {
102
102
  zIndex: "var(--z-overlay-mask)"
103
103
  },
104
- className: "bg-base/50 backdrop-blur-sm grid place-items-center",
104
+ className: "bg-tint-100/50 backdrop-blur-sm grid place-items-center",
105
105
  children: /*#__PURE__*/jsx(FloatingFocusManager, {
106
106
  context: context,
107
107
  children: /*#__PURE__*/jsxs("dialog", _objectSpread(_objectSpread({
@@ -94,7 +94,7 @@ var Flyout = function Flyout(_ref) {
94
94
  style: {
95
95
  zIndex: "var(--z-overlay-mask)"
96
96
  },
97
- className: "bg-base/10 backdrop-blur-sm transition-all",
97
+ className: "bg-tint-100/10 backdrop-blur-sm transition-all",
98
98
  children: children
99
99
  });
100
100
  }, [hideMask, handleClose]);
@@ -115,6 +115,7 @@ var Flyout = function Flyout(_ref) {
115
115
  children: /*#__PURE__*/jsxs(HotKeyWrapper, {
116
116
  children: [/*#__PURE__*/jsxs(Header, {
117
117
  flex: true,
118
+ align: "center",
118
119
  className: classNames("fabric-flyoutHeader", headerClassName),
119
120
  children: [isString(icon) && /*#__PURE__*/jsx(Icon, {
120
121
  icon: icon,
@@ -80,7 +80,7 @@ var Modal = function Modal(_ref) {
80
80
  style: {
81
81
  zIndex: "var(--z-overlay-mask)"
82
82
  },
83
- className: "bg-base/20 backdrop-blur-sm grid place-items-center",
83
+ className: "bg-tint-100/20 backdrop-blur-sm grid place-items-center",
84
84
  children: /*#__PURE__*/jsx(FloatingFocusManager, {
85
85
  context: context,
86
86
  children: /*#__PURE__*/jsx("dialog", _objectSpread(_objectSpread(_objectSpread({
@@ -96,6 +96,7 @@ var Modal = function Modal(_ref) {
96
96
  children: /*#__PURE__*/jsxs(HotKeyWrapper, {
97
97
  children: [/*#__PURE__*/jsxs(Header, {
98
98
  flex: true,
99
+ align: "center",
99
100
  className: classNames("fabric-modalHeader", headerClassName),
100
101
  children: [isString(icon) && /*#__PURE__*/jsx(Icon, {
101
102
  icon: icon,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-fabric/core",
3
- "version": "1.0.0-beta.39",
3
+ "version": "1.0.0-beta.40",
4
4
  "description": "RUF: Core package",
5
5
  "author": "Adarsh Pastakia <adarshpastakia@outlook.com>",
6
6
  "license": "MIT",
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@floating-ui/react": "^0.26.24",
39
- "@react-fabric/utilities": "^1.0.0-beta.39",
39
+ "@react-fabric/utilities": "^1.0.0-beta.40",
40
40
  "@tailwindcss/postcss": "~4.0.14",
41
41
  "boring-avatars": "^1.10.2",
42
42
  "classnames": "^2.5.1",
@@ -54,5 +54,5 @@
54
54
  "node": ">=18.12.0",
55
55
  "npm": ">=8.19.0"
56
56
  },
57
- "gitHead": "e528b7401ec10034422e0b0c3c6c118f3b24452c"
57
+ "gitHead": "ede05c84683697d85e28a0ae380cb9b74d693f51"
58
58
  }
@@ -118,7 +118,7 @@ export const AnimationBars = ({
118
118
  >
119
119
  <div
120
120
  className={classNames(
121
- "outline outline-tint-500/20 p-4 bg-base/50 backdrop-blur-sm rounded",
121
+ "outline outline-tint-500/20 p-4 bg-tint-50/50 backdrop-blur-sm rounded",
122
122
  )}
123
123
  >
124
124
  <div
@@ -146,7 +146,7 @@ export const AnimationSpinner = ({
146
146
  >
147
147
  <div
148
148
  className={classNames(
149
- "outline outline-tint-500/20 p-4 bg-base/50 backdrop-blur-sm rounded",
149
+ "outline outline-tint-500/20 p-4 bg-tint-50/50 backdrop-blur-sm rounded",
150
150
  )}
151
151
  >
152
152
  <div
@@ -47,8 +47,8 @@ export const DropdownTool = ({
47
47
  <Icon
48
48
  icon={icon ?? CoreIcons.caretDown}
49
49
  className={classNames(
50
+ "outline bg-tint-50/50 flex-content pointer-events-auto cursor-pointer",
50
51
  className,
51
- "outline bg-base/50 flex-content pointer-events-auto cursor-pointer",
52
52
  groupHover &&
53
53
  "hidden group-hover/tool:inline-block data-[dropdown-open]:inline-block",
54
54
  )}
@@ -23,10 +23,11 @@
23
23
 
24
24
  import { getImageColorset } from "@react-fabric/utilities";
25
25
  import classNames from "classnames";
26
- import { useMemo, useRef, useState } from "react";
26
+ import { Fragment, useEffect, useRef, useState } from "react";
27
27
  import { type AriaProps, type CssProp, type TestProps } from "../../types";
28
28
  import { CoreIcons } from "../../types/icons";
29
29
  import { Button } from "../button/Button";
30
+ import { Icon } from "../icon/Icon";
30
31
 
31
32
  export interface BaseProps extends CssProp, TestProps, AriaProps {
32
33
  /**
@@ -70,6 +71,17 @@ export interface VideoProps extends BaseProps {
70
71
  playOnHover?: boolean;
71
72
  }
72
73
 
74
+ const loadIndicator = (
75
+ <div
76
+ className={classNames(
77
+ "fabric-mediaPlaceholder",
78
+ "absolute z-1 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-4xl text-gray animate-pulse",
79
+ )}
80
+ >
81
+ ●●●
82
+ </div>
83
+ );
84
+
73
85
  export const Image = ({
74
86
  src,
75
87
  alt,
@@ -79,19 +91,12 @@ export const Image = ({
79
91
  ...aria
80
92
  }: ImageProps) => {
81
93
  const [colorSet, setColorSet] = useState("light");
82
- const loadIndicator = useMemo(
83
- () => (
84
- <div
85
- className={classNames(
86
- "fabric-mediaPlaceholder",
87
- "absolute z-1 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-4xl text-gray animate-pulse",
88
- )}
89
- >
90
- ●●●
91
- </div>
92
- ),
93
- [src],
94
- );
94
+ const [loaded, setLoaded] = useState(false);
95
+ const [errored, setErrored] = useState(false);
96
+ useEffect(() => {
97
+ setLoaded(false);
98
+ setErrored(false);
99
+ }, [src]);
95
100
  return (
96
101
  <div
97
102
  data-ref="image"
@@ -104,22 +109,32 @@ export const Image = ({
104
109
  data-colorset={colorSet}
105
110
  {...aria}
106
111
  >
107
- {loadIndicator}
108
- <img
109
- alt={alt ?? src}
110
- src={src}
111
- loading="lazy"
112
- crossOrigin="anonymous"
113
- className={classNames(
114
- "fabric-media",
115
- "pointer-events-none w-full h-full",
116
- cover[fit],
117
- )}
118
- onLoad={(e) => {
119
- e.currentTarget.previousElementSibling?.remove();
120
- setColorSet(getImageColorset(e.currentTarget));
121
- }}
122
- />
112
+ {!loaded && !errored && loadIndicator}
113
+ {!errored && (
114
+ <img
115
+ alt={alt ?? src}
116
+ src={src}
117
+ loading="lazy"
118
+ crossOrigin="anonymous"
119
+ className={classNames(
120
+ "fabric-media",
121
+ "pointer-events-none w-full h-full",
122
+ cover[fit],
123
+ )}
124
+ onLoad={(e) => {
125
+ setLoaded(true);
126
+ setColorSet(getImageColorset(e.currentTarget));
127
+ }}
128
+ onError={() => setErrored(true)}
129
+ />
130
+ )}
131
+ {errored && (
132
+ <Icon
133
+ size="2rem"
134
+ className="absolute text-tint-300 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"
135
+ icon={CoreIcons.mediaImageBroken}
136
+ />
137
+ )}
123
138
  </div>
124
139
  );
125
140
  };
@@ -135,10 +150,20 @@ export const Video = ({
135
150
  ...aria
136
151
  }: VideoProps) => {
137
152
  const videoRef = useRef<HTMLVideoElement>(null);
153
+ const [loaded, setLoaded] = useState(false);
154
+ const [errored, setErrored] = useState(false);
155
+ useEffect(() => {
156
+ setLoaded(false);
157
+ setErrored(false);
158
+ }, [src]);
138
159
 
139
160
  const [state, setState] = useState(true);
140
161
  const [volume, setVolume] = useState(false);
141
162
 
163
+ useEffect(() => {
164
+ setErrored(false);
165
+ }, [src]);
166
+
142
167
  return (
143
168
  <div
144
169
  data-ref="video"
@@ -156,74 +181,79 @@ export const Video = ({
156
181
  }}
157
182
  {...aria}
158
183
  >
159
- <div
160
- className={classNames(
161
- "fabric-mediaPlaceholder",
162
- "absolute z-1 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-5xl animate-pulse",
163
- )}
164
- >
165
- ●●●
166
- </div>
167
- <video
168
- loop
169
- ref={videoRef}
170
- controls={false}
171
- poster={poster}
172
- autoPlay={autoPlay}
173
- className={classNames(
174
- "fabric-media",
175
- "pointer-events-none w-full h-full",
176
- fit && `object-${fit}`,
177
- )}
178
- onPlay={() => setState(true)}
179
- onPause={() => setState(false)}
180
- onVolumeChange={(e) => setVolume(!!e.currentTarget.volume)}
181
- onLoadedData={(e) => {
182
- e.currentTarget.volume = 0;
183
- e.currentTarget.previousElementSibling?.remove();
184
- }}
185
- >
186
- <source src={src} />
187
- <track kind="captions" />
188
- </video>
189
- <div
190
- data-inner-clickable
191
- className={classNames(
192
- "fabric-mediaControls",
193
- "absolute bottom-6 end-6 rounded-full flex flex-col",
194
- )}
195
- >
196
- <Button
197
- rounded
198
- variant="link"
199
- className={"fabric-mediaControlPlaceholder"}
200
- icon={CoreIcons.ellipsis}
201
- aria-label="controls"
202
- />
203
- <Button
204
- rounded
205
- variant="link"
206
- aria-label="controls"
207
- className={"fabric-mediaControlAction"}
208
- onClick={() => {
209
- videoRef.current &&
210
- (videoRef.current.volume = videoRef.current.volume ? 0 : 1);
211
- }}
212
- icon={volume ? CoreIcons.volumeOn : CoreIcons.volumeOff}
213
- />
214
- <Button
215
- rounded
216
- variant="link"
217
- aria-label="controls"
218
- className={"fabric-mediaControlAction"}
219
- onClick={() => {
220
- videoRef.current?.paused
221
- ? videoRef.current?.play()
222
- : videoRef.current?.pause();
223
- }}
224
- icon={state ? CoreIcons.pause : CoreIcons.play}
184
+ {!loaded && !errored && loadIndicator}
185
+ {!errored && (
186
+ <Fragment>
187
+ <video
188
+ loop
189
+ ref={videoRef}
190
+ controls={false}
191
+ poster={poster}
192
+ autoPlay={autoPlay}
193
+ className={classNames(
194
+ "fabric-media",
195
+ "pointer-events-none w-full h-full",
196
+ fit && `object-${fit}`,
197
+ )}
198
+ onPlay={() => setState(true)}
199
+ onPause={() => setState(false)}
200
+ onVolumeChange={(e) => setVolume(!!e.currentTarget.volume)}
201
+ onLoadedData={(e) => {
202
+ e.currentTarget.volume = 0;
203
+ setLoaded(true);
204
+ }}
205
+ onError={() => setErrored(true)}
206
+ >
207
+ <source src={src} />
208
+ <track kind="captions" />
209
+ </video>
210
+ <div
211
+ data-inner-clickable
212
+ className={classNames(
213
+ "fabric-mediaControls",
214
+ "absolute bottom-6 end-6 rounded-full flex flex-col",
215
+ )}
216
+ >
217
+ <Button
218
+ rounded
219
+ variant="link"
220
+ className={"fabric-mediaControlPlaceholder"}
221
+ icon={CoreIcons.ellipsis}
222
+ aria-label="controls"
223
+ />
224
+ <Button
225
+ rounded
226
+ variant="link"
227
+ aria-label="controls"
228
+ className={"fabric-mediaControlAction"}
229
+ onClick={() => {
230
+ videoRef.current &&
231
+ (videoRef.current.volume = videoRef.current.volume ? 0 : 1);
232
+ }}
233
+ icon={volume ? CoreIcons.volumeOn : CoreIcons.volumeOff}
234
+ />
235
+ <Button
236
+ rounded
237
+ variant="link"
238
+ aria-label="controls"
239
+ className={"fabric-mediaControlAction"}
240
+ onClick={() => {
241
+ videoRef.current?.paused
242
+ ? videoRef.current?.play()
243
+ : videoRef.current?.pause();
244
+ }}
245
+ icon={state ? CoreIcons.pause : CoreIcons.play}
246
+ />
247
+ </div>
248
+ </Fragment>
249
+ )}
250
+ {errored && (
251
+ <Icon
252
+ size="2rem"
253
+ className="absolute text-tint-300 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"
254
+ icon={CoreIcons.mediaVideoBroken}
225
255
  />
226
- </div>
256
+ )}
227
257
  </div>
228
258
  );
229
259
  };
@@ -275,6 +275,15 @@ export const Menu = (props: MenuProps) => {
275
275
  const parentId = useFloatingParentNodeId();
276
276
  const tree = useFloatingTree();
277
277
 
278
+ if (tree) {
279
+ return (
280
+ <MenuComponent
281
+ {...(props as AnyObject)}
282
+ menuClassName={props.className}
283
+ />
284
+ );
285
+ }
286
+
278
287
  if (parentId === null) {
279
288
  return (
280
289
  <FloatingTree>
@@ -286,14 +295,5 @@ export const Menu = (props: MenuProps) => {
286
295
  );
287
296
  }
288
297
 
289
- if (tree) {
290
- return (
291
- <MenuComponent
292
- {...(props as AnyObject)}
293
- menuClassName={props.className}
294
- />
295
- );
296
- }
297
-
298
298
  return <MenuComponent {...props} />;
299
299
  };
@@ -172,7 +172,9 @@ export const Tab = <Tag extends React.ElementType = "button">({
172
172
  />
173
173
  )}
174
174
  {actions && active && (
175
- <DropdownTool className="bg-base/20 me-1 z-5">{actions}</DropdownTool>
175
+ <DropdownTool className="bg-tint-50/20 me-1 z-5">
176
+ {actions}
177
+ </DropdownTool>
176
178
  )}
177
179
  {onClose && (
178
180
  <span
@@ -54,7 +54,7 @@ export const HotKeyLabel = ({
54
54
  );
55
55
 
56
56
  return (
57
- <kbd className="hotkey-label inline-flex gap-1 select-none self-center font-mono items-center text-[0.625rem] leading-4 bg-tint-500/10 text-base/50 whitespace-nowrap rounded px-2 mx-1">
57
+ <kbd className="hotkey-label inline-flex gap-1 select-none self-center font-mono items-center text-[0.625rem] leading-4 bg-tint-500/20 text-tint-900/65 whitespace-nowrap rounded px-2 mx-1">
58
58
  {label}
59
59
  </kbd>
60
60
  );
@@ -76,7 +76,9 @@ export const HotKeyWrapper: FC<PropsWithChildren> = memo(
76
76
  const find = items.find(
77
77
  ({ key, alt, ctrl, meta, shift }: KeyValue) =>
78
78
  (key.toLowerCase() === keyCode.toLowerCase() ||
79
- key.toLowerCase() === code.toLowerCase()) &&
79
+ key.toLowerCase() === code.toLowerCase() ||
80
+ // work around for macos alt+key issue
81
+ `key${key.toLowerCase()}` === code.toLowerCase()) &&
80
82
  alt === altKey &&
81
83
  ctrl === ctrlKey &&
82
84
  meta === metaKey &&
@@ -156,7 +156,7 @@ export const Alert = ({
156
156
  <FloatingOverlay
157
157
  lockScroll
158
158
  style={{ zIndex: "var(--z-overlay-mask)" }}
159
- className="bg-base/50 backdrop-blur-sm grid place-items-center"
159
+ className="bg-tint-100/50 backdrop-blur-sm grid place-items-center"
160
160
  >
161
161
  <FloatingFocusManager context={context}>
162
162
  <dialog
@@ -202,7 +202,7 @@ export const Flyout = ({
202
202
  <FloatingOverlay
203
203
  lockScroll
204
204
  style={{ zIndex: "var(--z-overlay-mask)" }}
205
- className="bg-base/10 backdrop-blur-sm transition-all"
205
+ className="bg-tint-100/10 backdrop-blur-sm transition-all"
206
206
  >
207
207
  {children}
208
208
  </FloatingOverlay>
@@ -239,6 +239,7 @@ export const Flyout = ({
239
239
  <HotKeyWrapper>
240
240
  <Header
241
241
  flex
242
+ align="center"
242
243
  className={classNames("fabric-flyoutHeader", headerClassName)}
243
244
  >
244
245
  {isString(icon) && (
@@ -177,7 +177,7 @@ export const Modal = ({
177
177
  <FloatingOverlay
178
178
  lockScroll
179
179
  style={{ zIndex: "var(--z-overlay-mask)" }}
180
- className="bg-base/20 backdrop-blur-sm grid place-items-center"
180
+ className="bg-tint-100/20 backdrop-blur-sm grid place-items-center"
181
181
  >
182
182
  <FloatingFocusManager context={context}>
183
183
  <dialog
@@ -204,6 +204,7 @@ export const Modal = ({
204
204
  <HotKeyWrapper>
205
205
  <Header
206
206
  flex
207
+ align="center"
207
208
  className={classNames("fabric-modalHeader", headerClassName)}
208
209
  >
209
210
  {isString(icon) && (