@moises.ai/design-system 3.11.13 → 3.11.16

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.
@@ -1,7 +1,7 @@
1
1
  import * as a from "react";
2
2
  import j from "react";
3
- import { R as Vo, u as Te, C as _r, P as F, a as te, b as wr, T as Ir, c as se, d as Go, e as Pe, f as ne, g as B, h as Ee, i as N, A as Tr, j as Ar, k as Sr, G as xr, I as Nr, l as Pr, L as Fr, m as Or, n as ct, o as Dr, p as kr, q as Lr, S as Vr, r as Gr, s as Kr, t as $r, v as Fe, w as Br, x as pe, y as zr, z as Oe, B as Ao, D as jr, E as ie, F as Hr, H as Ur, J as Wr, K as Ko, M as $o, N as qr, O as Yr, Q as Zr, U as Bo } from "./index-DFiQTptM.js";
4
- import { V as Ni, W as Pi, X as Fi, Y as Oi, Z as Di, _ as ki, $ as Li, a0 as Vi, a1 as Gi, a2 as Ki, a3 as $i, a4 as Bi, a5 as zi, a6 as ji, a7 as Hi, a8 as Ui, a9 as Wi, aa as qi, ab as Yi, ac as Zi, ad as Xi, ae as Ji, af as Qi } from "./index-DFiQTptM.js";
3
+ import { R as Vo, u as Te, C as _r, P as F, a as te, b as wr, T as Ir, c as se, d as Go, e as Pe, f as ne, g as B, h as Ee, i as N, A as Tr, j as Ar, k as Sr, G as xr, I as Nr, l as Pr, L as Fr, m as Or, n as ct, o as Dr, p as kr, q as Lr, S as Vr, r as Gr, s as Kr, t as $r, v as Fe, w as Br, x as pe, y as zr, z as Oe, B as Ao, D as jr, E as ie, F as Hr, H as Ur, J as Wr, K as Ko, M as $o, N as qr, O as Yr, Q as Zr, U as Bo } from "./index-Dwaw5zqT.js";
4
+ import { V as Ni, W as Pi, X as Fi, Y as Oi, Z as Di, _ as ki, $ as Li, a0 as Vi, a1 as Gi, a2 as Ki, a3 as $i, a4 as Bi, a5 as zi, a6 as ji, a7 as Hi, a8 as Ui, a9 as Wi, aa as qi, ab as Yi, ac as Zi, ad as Xi, ae as Ji, af as Qi } from "./index-Dwaw5zqT.js";
5
5
  import { jsxs as je, Fragment as Ae, jsx as s } from "react/jsx-runtime";
6
6
  import Xr, { flushSync as de } from "react-dom";
7
7
  var Jr = "AccessibleIcon", lt = ({ children: e, label: t }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moises.ai/design-system",
3
- "version": "3.11.13",
3
+ "version": "3.11.16",
4
4
  "description": "Design System package based on @radix-ui/themes with custom defaults",
5
5
  "private": false,
6
6
  "type": "module",
@@ -0,0 +1,204 @@
1
+ import { useMemo } from 'react'
2
+ import { Box, Flex, Heading, Text, Grid, Card } from '@radix-ui/themes'
3
+ import cssSource from './custom-styles.css?raw'
4
+
5
+ function isColorValue(value) {
6
+ const v = value.trim()
7
+ return v.startsWith('#') || /^rgba?\(/i.test(v)
8
+ }
9
+
10
+ function clampByte(n) {
11
+ return Math.round(Math.min(255, Math.max(0, n)))
12
+ }
13
+
14
+ function rgbaToHex(r, g, b, a = 1) {
15
+ const toHex = (n) => clampByte(n).toString(16).padStart(2, '0').toUpperCase()
16
+ let hex = `#${toHex(r)}${toHex(g)}${toHex(b)}`
17
+ if (a < 1) {
18
+ hex += toHex(a * 255)
19
+ }
20
+ return hex
21
+ }
22
+
23
+ function parseColorFormats(cssValue) {
24
+ const v = cssValue.trim()
25
+ if (v.startsWith('#')) {
26
+ let h = v.slice(1)
27
+ if (h.length === 3) {
28
+ h = h
29
+ .split('')
30
+ .map((c) => c + c)
31
+ .join('')
32
+ }
33
+ if (h.length === 6) {
34
+ const r = parseInt(h.slice(0, 2), 16)
35
+ const g = parseInt(h.slice(2, 4), 16)
36
+ const b = parseInt(h.slice(4, 6), 16)
37
+ return {
38
+ rgba: `rgba(${r}, ${g}, ${b}, 1)`,
39
+ hex: `#${h.toUpperCase()}`,
40
+ }
41
+ }
42
+ if (h.length === 8) {
43
+ const r = parseInt(h.slice(0, 2), 16)
44
+ const g = parseInt(h.slice(2, 4), 16)
45
+ const b = parseInt(h.slice(4, 6), 16)
46
+ const alpha = parseInt(h.slice(6, 8), 16) / 255
47
+ const aRounded = Math.round(alpha * 1000) / 1000
48
+ return {
49
+ rgba: `rgba(${r}, ${g}, ${b}, ${aRounded})`,
50
+ hex: `#${h.toUpperCase()}`,
51
+ }
52
+ }
53
+ return { rgba: v, hex: v }
54
+ }
55
+
56
+ const m = v.match(
57
+ /^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)(?:\s*,\s*([\d.]+))?\s*\)$/i,
58
+ )
59
+ if (m) {
60
+ const r = Number(m[1])
61
+ const g = Number(m[2])
62
+ const b = Number(m[3])
63
+ const a = m[4] !== undefined ? Number(m[4]) : 1
64
+ const aRounded = Math.round(a * 1000) / 1000
65
+ return {
66
+ rgba: `rgba(${r}, ${g}, ${b}, ${aRounded})`,
67
+ hex: rgbaToHex(r, g, b, a),
68
+ }
69
+ }
70
+
71
+ return { rgba: v, hex: '—' }
72
+ }
73
+
74
+ function parseColorTokens(css) {
75
+ const lines = css.split('\n')
76
+ /** @type {{ section: string; name: string; value: string }[]} */
77
+ const tokens = []
78
+ let section = 'Cores'
79
+
80
+ for (const line of lines) {
81
+ const trimmed = line.trim()
82
+ const sectionMatch = trimmed.match(/^\/\*\s*([^*]+?)\s*\*\/$/)
83
+ if (sectionMatch && !trimmed.includes('--')) {
84
+ section = sectionMatch[1].trim()
85
+ continue
86
+ }
87
+
88
+ const varMatch = trimmed.match(/^--([\w-]+)\s*:\s*([^;]+);/)
89
+ if (!varMatch) continue
90
+
91
+ const name = `--${varMatch[1]}`
92
+ const value = varMatch[2].trim()
93
+ if (value.startsWith('var(')) continue
94
+ if (!isColorValue(value)) continue
95
+
96
+ tokens.push({ section, name, value })
97
+ }
98
+
99
+ return tokens
100
+ }
101
+
102
+ function groupBySection(tokens) {
103
+ /** @type {Record<string, typeof tokens>} */
104
+ const map = {}
105
+ for (const t of tokens) {
106
+ if (!map[t.section]) map[t.section] = []
107
+ map[t.section].push(t)
108
+ }
109
+ return map
110
+ }
111
+
112
+ export default {
113
+ title: 'Colors',
114
+ parameters: {
115
+ layout: 'fullscreen',
116
+ },
117
+ }
118
+
119
+ export const Colors = () => {
120
+ const sections = useMemo(() => {
121
+ const tokens = parseColorTokens(cssSource)
122
+ return groupBySection(tokens)
123
+ }, [])
124
+
125
+ return (
126
+ <Box p="6" style={{ maxWidth: 1200, margin: '0 auto' }}>
127
+ <Flex direction="column" gap="6">
128
+ <Flex direction="column" gap="2">
129
+ <Heading size="8">Cores</Heading>
130
+ <Text size="3" color="gray">
131
+ Variáveis CSS do design system com rgba e hex.
132
+ </Text>
133
+ </Flex>
134
+
135
+ {Object.entries(sections).map(([section, tokens]) => (
136
+ <Flex key={section} direction="column" gap="3">
137
+ <Heading size="5">{section}</Heading>
138
+ <Grid columns="1" gap="2">
139
+ {tokens.map(({ name, value }) => {
140
+ const { rgba, hex } = parseColorFormats(value)
141
+ return (
142
+ <Card key={name} size="1">
143
+ <Grid
144
+ columns={{ initial: '1', sm: 'auto 1fr' }}
145
+ gap="3"
146
+ align="center"
147
+ >
148
+ <Box
149
+ style={{
150
+ width: '100%',
151
+ minWidth: 56,
152
+ maxWidth: 120,
153
+ height: 48,
154
+ borderRadius: 8,
155
+ backgroundColor: `var(${name})`,
156
+ boxShadow:
157
+ 'inset 0 0 0 1px rgba(255, 255, 255, 0.12)',
158
+ }}
159
+ aria-hidden
160
+ />
161
+ <Grid
162
+ columns={{ initial: '1', xs: '1fr 1fr' }}
163
+ gap="2"
164
+ width="100%"
165
+ >
166
+ <Flex direction="column" gap="1" minWidth="0">
167
+ <Text
168
+ size="2"
169
+ weight="medium"
170
+ style={{
171
+ fontFamily: 'ui-monospace, monospace',
172
+ wordBreak: 'break-all',
173
+ }}
174
+ >
175
+ {name}
176
+ </Text>
177
+ <Text
178
+ size="1"
179
+ color="gray"
180
+ style={{ fontFamily: 'ui-monospace, monospace' }}
181
+ >
182
+ {rgba}
183
+ </Text>
184
+ </Flex>
185
+ <Flex align="center">
186
+ <Text
187
+ size="2"
188
+ style={{ fontFamily: 'ui-monospace, monospace' }}
189
+ >
190
+ {hex}
191
+ </Text>
192
+ </Flex>
193
+ </Grid>
194
+ </Grid>
195
+ </Card>
196
+ )
197
+ })}
198
+ </Grid>
199
+ </Flex>
200
+ ))}
201
+ </Flex>
202
+ </Box>
203
+ )
204
+ }
@@ -0,0 +1,72 @@
1
+ import classNames from 'classnames'
2
+ import { ContextMenu as ContextMenuRadix } from 'radix-ui'
3
+ import React, { forwardRef, memo } from 'react'
4
+ import { createRenderItem, styles } from '../../lib/menu'
5
+ import { CanvasContextMenuTrigger } from './CanvasContextMenuTrigger'
6
+
7
+ const renderOption = createRenderItem(ContextMenuRadix)
8
+
9
+ /**
10
+ * Variant of ContextMenu for programmatic opening from non-DOM event sources
11
+ * (e.g. Pixi canvas). Instead of wrapping a visible trigger element, it exposes
12
+ * a ref to an internal hidden div that can receive a synthetic `contextmenu` event.
13
+ *
14
+ * Usage:
15
+ * const triggerRef = useRef(null)
16
+ * triggerRef.current?.dispatchEvent(
17
+ * new MouseEvent('contextmenu', { bubbles: true, clientX: x, clientY: y })
18
+ * )
19
+ * ...
20
+ * <CanvasContextMenu ref={triggerRef} options={[...]}>
21
+ * {children}
22
+ * </CanvasContextMenu>
23
+ */
24
+ export const CanvasContextMenu = memo(
25
+ forwardRef(function CanvasContextMenu(
26
+ {
27
+ options,
28
+ className,
29
+ size = '2',
30
+ side,
31
+ align,
32
+ onSelectionChange,
33
+ closeOnSelect = true,
34
+ },
35
+ ref,
36
+ ) {
37
+ return (
38
+ <ContextMenuRadix.Root>
39
+ <ContextMenuRadix.Trigger asChild>
40
+ <div
41
+ ref={ref}
42
+ style={{
43
+ position: 'fixed',
44
+ width: 0,
45
+ height: 0,
46
+ pointerEvents: 'none',
47
+ }}
48
+ />
49
+ </ContextMenuRadix.Trigger>
50
+
51
+ {options?.length > 0 && (
52
+ <ContextMenuRadix.Content
53
+ className={classNames(styles.menuContent, className)}
54
+ side={side}
55
+ align={align}
56
+ sideOffset={5}
57
+ alignOffset={0}
58
+ onCloseAutoFocus={(event) => event.preventDefault()}
59
+ onClick={(event) => event.stopPropagation()}
60
+ >
61
+ {options?.map((option) =>
62
+ renderOption(option, size, onSelectionChange, closeOnSelect),
63
+ )}
64
+ </ContextMenuRadix.Content>
65
+ )}
66
+ </ContextMenuRadix.Root>
67
+ )
68
+ }),
69
+ )
70
+
71
+ CanvasContextMenu.displayName = 'CanvasContextMenu'
72
+ CanvasContextMenu.Trigger = CanvasContextMenuTrigger
@@ -0,0 +1,271 @@
1
+ import { Flex } from '@radix-ui/themes'
2
+ import React, { useCallback, useRef, useState } from 'react'
3
+ import { Button } from '../Button/Button'
4
+ import { CanvasContextMenu } from './CanvasContextMenu'
5
+
6
+ export default {
7
+ title: 'Components/CanvasContextMenu',
8
+ component: CanvasContextMenu,
9
+ parameters: {
10
+ layout: 'centered',
11
+ docs: {
12
+ description: {
13
+ component: `
14
+ CanvasContextMenu provides a list of actions or options that appear when right-clicking on a canvas element. It's built on top of Radix UI's ContextMenu component and supports various configurations including submenus, separators, labels, and keybindings.
15
+
16
+ ## Usage
17
+
18
+ \`\`\`jsx
19
+ import { ContextMenu } from '@moises.ai/design-system'
20
+
21
+ const MyComponent = () => (
22
+ <ContextMenu
23
+ options={[
24
+ {
25
+ type: 'item',
26
+ key: 'edit',
27
+ label: 'Edit',
28
+ onClick: () => console.log('Edit clicked'),
29
+ },
30
+ {
31
+ type: 'item',
32
+ key: 'duplicate',
33
+ label: 'Duplicate',
34
+ onClick: () => console.log('Duplicate clicked'),
35
+ },
36
+ { type: 'separator', key: 'sep1' },
37
+ {
38
+ type: 'item',
39
+ key: 'delete',
40
+ label: 'Delete',
41
+ onClick: () => console.log('Delete clicked'),
42
+ },
43
+ ]}
44
+ onSelectionChange={(selectedItem) => {
45
+ console.log('Selected item:', selectedItem)
46
+ // Handle the selected item
47
+ }}
48
+ disable={false} // Optional: prevents context menu from opening when true
49
+ >
50
+ <canvas />
51
+ </ContextMenu>
52
+ )
53
+ \`\`\`
54
+
55
+ ## Options API
56
+
57
+ The \`options\` prop accepts an array of objects with the following types:
58
+
59
+ ### Menu Item
60
+ \`\`\`js
61
+ {
62
+ type: 'item',
63
+ key: 'uniqueKey', // Required unique identifier
64
+ label: 'Item Label', // Text to display
65
+ secondaryText: 'Secondary text', // Optional, secondary text
66
+ secondaryTextPosition: 'bottom' | 'right', // Optional, position of secondary text (default: 'bottom')
67
+ icon: <Icon />, // Optional, icon displayed before the label
68
+ onClick: () => {}, // Function called when clicked
69
+ disabled: false, // Optional, disables the item when true
70
+ keybinding: '⌘C', // Optional, displays a keyboard shortcut
71
+ rightIcon: <Icon />, // Optional, icon displayed on the right side
72
+ color: 'red' | 'cyan', // Optional: custom color for this item
73
+ }
74
+ \`\`\`
75
+
76
+ ### Submenu
77
+ \`\`\`js
78
+ {
79
+ type: 'sub',
80
+ key: 'uniqueKey', // Required unique identifier
81
+ label: 'Submenu Label', // Text to display
82
+ children: [], // Array of menu items for the submenu
83
+ color: 'red' | 'cyan', // Optional: custom color for this submenu trigger
84
+ }
85
+ \`\`\`
86
+
87
+ ### Separator
88
+ \`\`\`js
89
+ {
90
+ type: 'separator',
91
+ key: 'uniqueKey', // Required unique identifier
92
+ }
93
+ \`\`\`
94
+
95
+ ### Label
96
+ \`\`\`js
97
+ {
98
+ type: 'label',
99
+ key: 'uniqueKey', // Required unique identifier
100
+ label: 'Section Label', // Text to display as a non-interactive label
101
+ color: 'red' | 'cyan', // Optional: custom color for this label
102
+ }
103
+ \`\`\`
104
+
105
+ ### Checkbox
106
+ \`\`\`js
107
+ {
108
+ type: 'checkbox',
109
+ key: 'uniqueKey', // Required unique identifier
110
+ label: 'Checkbox Label', // Text to display
111
+ secondaryText: 'Secondary text', // Optional, secondary text
112
+ secondaryTextPosition: 'bottom' | 'right', // Optional, position of secondary text (default: 'bottom')
113
+ icon: <Icon />, // Optional, icon displayed after the checkmark placeholder
114
+ checked: true, // Whether the checkbox is checked (always reserves space for checkmark)
115
+ onChange: (checked) => {}, // Function called when toggled, receives new checked state
116
+ disabled: false, // Optional, disables the checkbox when true
117
+ keybinding: '⌘C', // Optional, displays a keyboard shortcut
118
+ rightIcon: <Icon />, // Optional, icon displayed on the right side
119
+ color: 'red' | 'cyan', // Optional: custom color for this checkbox item
120
+ }
121
+ \`\`\`
122
+ `,
123
+ },
124
+ },
125
+ },
126
+ tags: ['autodocs'],
127
+ argTypes: {
128
+ children: {
129
+ control: false,
130
+ description: 'The element to attach the context menu to **(REQUIRED)**',
131
+ table: {
132
+ type: { summary: 'React.ReactNode' },
133
+ },
134
+ },
135
+ options: {
136
+ control: false,
137
+ description: 'Array of menu items configuration **(REQUIRED)**',
138
+ table: {
139
+ type: { summary: 'Array<MenuItem>' },
140
+ },
141
+ },
142
+ side: {
143
+ options: ['top', 'right', 'bottom', 'left'],
144
+ control: { type: 'select' },
145
+ description: 'The preferred side of the trigger to render the menu',
146
+ table: {
147
+ type: { summary: 'string' },
148
+ defaultValue: { summary: 'bottom' },
149
+ },
150
+ },
151
+ align: {
152
+ options: ['start', 'center', 'end'],
153
+ control: { type: 'select' },
154
+ description: 'The preferred alignment against the trigger',
155
+ table: {
156
+ type: { summary: 'string' },
157
+ defaultValue: { summary: 'end' },
158
+ },
159
+ },
160
+ size: {
161
+ options: ['1', '2'],
162
+ control: { type: 'select' },
163
+ description: 'The size of the context menu',
164
+ table: {
165
+ type: { summary: 'string' },
166
+ defaultValue: { summary: '2' },
167
+ },
168
+ },
169
+ className: {
170
+ control: false,
171
+ description: 'Additional CSS class names to apply to the context menu',
172
+ table: {
173
+ type: { summary: 'string' },
174
+ defaultValue: { summary: 'undefined' },
175
+ },
176
+ },
177
+ onSelectionChange: {
178
+ control: false,
179
+ description:
180
+ 'Callback function called when any menu item is selected, receives the selected item as parameter',
181
+ table: {
182
+ type: { summary: 'function' },
183
+ defaultValue: { summary: 'undefined' },
184
+ },
185
+ },
186
+ closeOnSelect: {
187
+ control: { type: 'boolean' },
188
+ description:
189
+ 'When false, keeps the menu open after selecting an option (handy for performing multiple actions)',
190
+ table: {
191
+ type: { summary: 'boolean' },
192
+ defaultValue: { summary: 'true' },
193
+ },
194
+ },
195
+ disable: {
196
+ control: { type: 'boolean' },
197
+ description:
198
+ 'When true, prevents the context menu from opening and renders only the children',
199
+ table: {
200
+ type: { summary: 'boolean' },
201
+ defaultValue: { summary: 'false' },
202
+ },
203
+ },
204
+ },
205
+ }
206
+
207
+ export const Default = {
208
+ args: {
209
+ side: 'bottom',
210
+ align: 'end',
211
+ options: [
212
+ {
213
+ type: 'item',
214
+ key: 'item1',
215
+ label: 'Edit',
216
+ onClick: () => console.log('Edit clicked'),
217
+ },
218
+ {
219
+ type: 'item',
220
+ key: 'item2',
221
+ label: 'Duplicate',
222
+ onClick: () => console.log('Duplicate clicked'),
223
+ },
224
+ { type: 'separator', key: 'sep1' },
225
+ {
226
+ type: 'item',
227
+ key: 'item3',
228
+ label: 'Delete',
229
+ onClick: () => console.log('Delete clicked'),
230
+ },
231
+ ],
232
+ },
233
+ render: (args) => {
234
+ const [count, setCount] = useState(0)
235
+ const triggerRef = useRef(null)
236
+
237
+ const dispatch = useCallback(({ x, y }) => {
238
+ triggerRef.current?.dispatchEvent(
239
+ new MouseEvent('contextmenu', {
240
+ bubbles: true,
241
+ cancelable: true,
242
+ clientX: x,
243
+ clientY: y,
244
+ }),
245
+ )
246
+ }, [])
247
+
248
+ return (
249
+ <Flex justify="center" align="start" height="300px">
250
+ <CanvasContextMenu ref={triggerRef} {...args} />
251
+
252
+ <CanvasContextMenu.Trigger>
253
+ <div
254
+ style={{
255
+ width: 100,
256
+ height: 100,
257
+ border: '1px dashed gray',
258
+ }}
259
+ onContextMenu={(e) => dispatch({ x: e.clientX, y: e.clientY })}
260
+ >
261
+ {count}
262
+ </div>
263
+ </CanvasContextMenu.Trigger>
264
+
265
+ <Button onClick={() => setCount((count) => count + 1)}>
266
+ Trigger Change
267
+ </Button>
268
+ </Flex>
269
+ )
270
+ },
271
+ }
@@ -0,0 +1,17 @@
1
+ import React from 'react'
2
+
3
+ export function CanvasContextMenuTrigger({ children }) {
4
+ return (
5
+ // The outer div catches the bubbling contextmenu event AFTER Radix has already
6
+ // processed it on the trigger, suppressing the browser's native context menu
7
+ // without interfering with Radix's own handler.
8
+ <div
9
+ onContextMenu={(e) => e.preventDefault()}
10
+ style={{ display: 'contents' }}
11
+ >
12
+ {children}
13
+ </div>
14
+ )
15
+ }
16
+
17
+ CanvasContextMenuTrigger.displayName = 'CanvasContextMenuTrigger'
@@ -0,0 +1,15 @@
1
+ export const MinusIcon = ({ width = 16, height = 16, className, ...props }) => (
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ width={width}
5
+ height={height}
6
+ viewBox="0 0 16 16"
7
+ fill="none"
8
+ className={className} {...props}>
9
+ <g >
10
+ <path d="M3.33325 8H12.6666" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" />
11
+ </g>
12
+ </svg>
13
+ )
14
+
15
+ MinusIcon.displayName = 'MinusIcon'
package/src/icons.jsx CHANGED
@@ -351,3 +351,4 @@ export { ChevronRightFilledIcon } from './icons/ChevronRightFilledIcon'
351
351
  export { ChevronDownFilledIcon } from './icons/ChevronDownFilledIcon'
352
352
  export { Share2Icon } from './icons/Share2Icon'
353
353
  export { TrackPreviousIcon } from './icons/TrackPreviousIcon'
354
+ export { MinusIcon } from './icons/MinusIcon'
package/src/index.jsx CHANGED
@@ -78,10 +78,10 @@ export { AdditionalItems } from './components/AdditionalItems/AdditionalItems'
78
78
  export { BannerAnnouncement } from './components/BannerAnnouncement/BannerAnnouncement'
79
79
  export { Button } from './components/Button/Button'
80
80
  export { Callout } from './components/Callout/Callout'
81
+ export { CanvasContextMenu } from './components/CanvasContextMenu/CanvasContextMenu'
81
82
  export { Card as CardWidget } from './components/Card/Card'
82
83
  export { Checkbox } from './components/Checkbox/Checkbox'
83
84
  export { CigarBar } from './components/CigarBar/CigarBar'
84
- export { CanvasContextMenu } from './components/ContextMenu/CanvasContextMenu'
85
85
  export { ContextMenu } from './components/ContextMenu/ContextMenu'
86
86
  export { Countdown } from './components/Countdown/Countdown'
87
87
  export { DataTable } from './components/DataTable/DataTable'
@@ -1,73 +0,0 @@
1
- import classNames from 'classnames'
2
- import { ContextMenu as ContextMenuRadix } from 'radix-ui'
3
- import React, { forwardRef, memo } from 'react'
4
-
5
- import { createRenderItem, styles } from '../../lib/menu'
6
-
7
- const renderOption = createRenderItem(ContextMenuRadix)
8
-
9
- /**
10
- * Variant of ContextMenu for programmatic opening from non-DOM event sources
11
- * (e.g. Pixi canvas). Instead of wrapping a visible trigger element, it exposes
12
- * a ref to an internal hidden div that can receive a synthetic `contextmenu` event.
13
- *
14
- * Usage:
15
- * const triggerRef = useRef(null)
16
- * triggerRef.current?.dispatchEvent(
17
- * new MouseEvent('contextmenu', { bubbles: true, clientX: x, clientY: y })
18
- * )
19
- * ...
20
- * <CanvasContextMenu ref={triggerRef} options={[...]}>
21
- * {children} // rendered between trigger and content, inside the same Root
22
- * </CanvasContextMenu>
23
- */
24
- export const CanvasContextMenu = memo(
25
- forwardRef(function CanvasContextMenu(
26
- {
27
- children,
28
- options,
29
- className,
30
- size = '2',
31
- onSelectionChange,
32
- closeOnSelect = true,
33
- ...props
34
- },
35
- ref,
36
- ) {
37
- return (
38
- // The outer div catches the bubbling contextmenu event AFTER Radix has already
39
- // processed it on the trigger, suppressing the browser's native context menu
40
- // without interfering with Radix's own handler.
41
- <div onContextMenu={(e) => e.preventDefault()} style={{ display: 'contents' }}>
42
- <ContextMenuRadix.Root>
43
- <ContextMenuRadix.Trigger asChild>
44
- <div
45
- ref={ref}
46
- style={{ position: 'fixed', width: 0, height: 0, pointerEvents: 'none' }}
47
- />
48
- </ContextMenuRadix.Trigger>
49
- {children}
50
- {options?.length > 0 && (
51
- <ContextMenuRadix.Content
52
- className={classNames(styles.menuContent, className)}
53
- side={props.side}
54
- align={props.align}
55
- sideOffset={5}
56
- alignOffset={0}
57
- onCloseAutoFocus={(event) => {
58
- event.preventDefault()
59
- }}
60
- onClick={(event) => event.stopPropagation()}
61
- >
62
- {options?.map((option) =>
63
- renderOption(option, size, onSelectionChange, closeOnSelect),
64
- )}
65
- </ContextMenuRadix.Content>
66
- )}
67
- </ContextMenuRadix.Root>
68
- </div>
69
- )
70
- }),
71
- )
72
-
73
- CanvasContextMenu.displayName = 'CanvasContextMenu'