@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.
- package/dist/{TrackPreviousIcon-QO8TnAez.js → MinusIcon-BS8c20DV.js} +1306 -1308
- package/dist/icons.js +1 -1
- package/dist/{index-DFiQTptM.js → index-Dwaw5zqT.js} +5 -5
- package/dist/index.js +1531 -1516
- package/dist/primitives.js +2 -2
- package/package.json +1 -1
- package/src/colors/Colors.stories.jsx +204 -0
- package/src/components/CanvasContextMenu/CanvasContextMenu.jsx +72 -0
- package/src/components/CanvasContextMenu/CanvasContextMenu.stories.jsx +271 -0
- package/src/components/CanvasContextMenu/CanvasContextMenuTrigger.jsx +17 -0
- package/src/icons/MinusIcon.jsx +15 -0
- package/src/icons.jsx +1 -0
- package/src/index.jsx +1 -1
- package/src/components/ContextMenu/CanvasContextMenu.jsx +0 -73
package/dist/primitives.js
CHANGED
|
@@ -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-
|
|
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-
|
|
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
|
@@ -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'
|