onchain-lexical-ui 0.0.1

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.
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ *
8
+ */
9
+ @import './const.less';
10
+
11
+ .Input__wrapper {
12
+ display: flex;
13
+ flex-direction: row;
14
+ align-items: center;
15
+ margin-bottom: 10px;
16
+ }
17
+ .Input__label {
18
+ display: flex;
19
+ flex: 1;
20
+ color: #666;
21
+ }
22
+ .Input__input {
23
+ display: flex;
24
+ flex: 2;
25
+ border: 1px solid #999;
26
+ padding-top: 7px;
27
+ padding-bottom: 7px;
28
+ padding-left: 10px;
29
+ padding-right: 10px;
30
+ font-size: 16px;
31
+ border-radius: 0px;
32
+ min-width: 0;
33
+ height: @inputHeight;
34
+ &:focus-visible {
35
+ outline: none;
36
+ border-color: @themeColor;
37
+ };
38
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ *
8
+ */
9
+
10
+ .KatexEquationAlterer_defaultRow {
11
+ display: flex;
12
+ flex-direction: row;
13
+ margin-top: 10px;
14
+ margin-bottom: 10px;
15
+ justify-content: space-between;
16
+ overflow: hidden;
17
+ }
18
+
19
+ .KatexEquationAlterer_dialogActions {
20
+ display: flex;
21
+ flex-direction: row;
22
+ overflow: hidden;
23
+ margin-top: 20px;
24
+ margin-bottom: 0;
25
+ justify-content: right;
26
+ }
27
+
28
+ .KatexEquationAlterer_centerRow {
29
+ display: flex;
30
+ flex-direction: 'row';
31
+ margin-top: 10px;
32
+ margin-bottom: 10px;
33
+ justify-content: center;
34
+ overflow: hidden;
35
+ }
36
+
37
+ .KatexEquationAlterer_textArea {
38
+ width: 100%;
39
+ resize: none;
40
+ padding: 7px;
41
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import type {JSX} from 'react';
10
+
11
+ import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
12
+ import * as React from 'react';
13
+ import {useCallback, useState} from 'react';
14
+ import {ErrorBoundary} from 'react-error-boundary';
15
+
16
+ import Button from './Button';
17
+ import Styles from './KatexEquationAlterer.module.less';
18
+ import KatexRenderer from './KatexRenderer';
19
+
20
+ type Props = {
21
+ initialEquation?: string;
22
+ onConfirm: (equation: string, inline: boolean) => void;
23
+ };
24
+
25
+ export default function KatexEquationAlterer({
26
+ onConfirm,
27
+ initialEquation = '',
28
+ }: Props): JSX.Element {
29
+ const [editor] = useLexicalComposerContext();
30
+ const [equation, setEquation] = useState<string>(initialEquation);
31
+ const [inline, setInline] = useState<boolean>(true);
32
+
33
+ const onClick = useCallback(() => {
34
+ onConfirm(equation, inline);
35
+ }, [onConfirm, equation, inline]);
36
+
37
+ const onCheckboxChange = useCallback(() => {
38
+ setInline(!inline);
39
+ }, [setInline, inline]);
40
+
41
+ return (
42
+ <>
43
+ <div className={Styles.KatexEquationAlterer_defaultRow}>
44
+ Inline
45
+ <input type="checkbox" checked={inline} onChange={onCheckboxChange} />
46
+ </div>
47
+ <div className={Styles.KatexEquationAlterer_defaultRow}>Equation </div>
48
+ <div className={Styles.KatexEquationAlterer_centerRow}>
49
+ {inline ? (
50
+ <input
51
+ onChange={(event) => {
52
+ setEquation(event.target.value);
53
+ }}
54
+ value={equation}
55
+ className={Styles.KatexEquationAlterer_textArea}
56
+ />
57
+ ) : (
58
+ <textarea
59
+ onChange={(event) => {
60
+ setEquation(event.target.value);
61
+ }}
62
+ value={equation}
63
+ className={Styles.KatexEquationAlterer_textArea}
64
+ />
65
+ )}
66
+ </div>
67
+ <div className={Styles.KatexEquationAlterer_defaultRow}>
68
+ Visualization{' '}
69
+ </div>
70
+ <div className={Styles.KatexEquationAlterer_centerRow}>
71
+ <ErrorBoundary onError={(e) => editor._onError(e)} fallback={null}>
72
+ <KatexRenderer
73
+ equation={equation}
74
+ inline={false}
75
+ onDoubleClick={() => null}
76
+ />
77
+ </ErrorBoundary>
78
+ </div>
79
+ <div className={Styles.KatexEquationAlterer_dialogActions}>
80
+ <Button onClick={onClick}>Confirm</Button>
81
+ </div>
82
+ </>
83
+ );
84
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import type {JSX} from 'react';
10
+
11
+ import katex from 'katex';
12
+ import * as React from 'react';
13
+ import {useEffect, useRef} from 'react';
14
+
15
+ export default function KatexRenderer({
16
+ equation,
17
+ inline,
18
+ height,
19
+ onDoubleClick,
20
+ }: Readonly<{
21
+ equation: string;
22
+ inline: boolean;
23
+ height?: React.CSSProperties['height'];
24
+ onDoubleClick: () => void;
25
+ }>): JSX.Element {
26
+ const katexElementRef = useRef(null);
27
+
28
+ useEffect(() => {
29
+ const katexElement = katexElementRef.current;
30
+
31
+ if (katexElement !== null) {
32
+ katex.render(equation, katexElement, {
33
+ displayMode: !inline, // true === block display //
34
+ errorColor: '#cc0000',
35
+ output: 'html',
36
+ strict: 'warn',
37
+ throwOnError: false,
38
+ trust: false,
39
+ });
40
+ }
41
+ }, [equation, inline]);
42
+
43
+ return (
44
+ // We use an empty image tag either side to ensure Android doesn't try and compose from the
45
+ // inner text from Katex. There didn't seem to be any other way of making this work,
46
+ // without having a physical space.
47
+ <>
48
+ <img
49
+ src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
50
+ width="0"
51
+ height="0"
52
+ alt=""
53
+ />
54
+ <span
55
+ role="button"
56
+ tabIndex={-1}
57
+ onDoubleClick={onDoubleClick}
58
+ ref={katexElementRef}
59
+ style={{
60
+ display: 'inline-block',
61
+ height,
62
+ width: !inline ? '100%' : undefined,
63
+ }}
64
+ />
65
+ <img
66
+ src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
67
+ width="0"
68
+ height="0"
69
+ alt=""
70
+ />
71
+ </>
72
+ );
73
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ *
8
+ */
9
+ @import './const.less';
10
+
11
+ .Modal__overlay {
12
+ display: flex;
13
+ justify-content: center;
14
+ align-items: center;
15
+ position: fixed;
16
+ flex-direction: column;
17
+ top: 0px;
18
+ bottom: 0px;
19
+ left: 0px;
20
+ right: 0px;
21
+ background-color: transparent;
22
+ flex-grow: 0px;
23
+ flex-shrink: 1px;
24
+ z-index: 999;
25
+ }
26
+ .Modal__modal {
27
+ min-height: 100px;
28
+ min-width: 300px;
29
+ display: flex;
30
+ flex-grow: 0px;
31
+ background-color: #fff;
32
+ flex-direction: column;
33
+ position: relative;
34
+ box-shadow: 0 0 10px 0px #00000024;
35
+ border: 1px solid @themeColor;;
36
+ }
37
+ .Modal__titleBox {
38
+ display: flex;
39
+ align-items: center;
40
+ height: 40px;
41
+ justify-content: space-between;
42
+ padding: 0 20px;
43
+ cursor: all-scroll;
44
+ border-bottom: 1px solid #ccc;
45
+ background-color: @themeColor;
46
+ }
47
+ .Modal__contentBox {
48
+ padding: 24px 20px;
49
+ }
50
+ .Modal__title {
51
+ color: #fff;
52
+ font-size: 13px;
53
+ font-weight: 600;
54
+ margin: 0px;
55
+ }
56
+ .Modal__closeButton {
57
+ color: #fff;
58
+ border: 0px;
59
+ justify-content: center;
60
+ align-items: center;
61
+ display: flex;
62
+ text-align: center;
63
+ cursor: pointer;
64
+ background-color: transparent;
65
+ }
package/src/Modal.tsx ADDED
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+ /* eslint-disable @typescript-eslint/no-unused-vars */
9
+ /* eslint-disable lexical/no-optional-chaining */
10
+
11
+ import type {JSX} from 'react';
12
+
13
+ import {isDOMNode} from 'lexical';
14
+ import * as React from 'react';
15
+ import {ReactNode, useEffect, useRef} from 'react';
16
+ import {createPortal} from 'react-dom';
17
+ import Draggable, {DraggableData, DraggableEvent} from 'react-draggable';
18
+
19
+ import Styles from './Modal.module.less';
20
+
21
+ function PortalImpl({
22
+ onClose,
23
+ children,
24
+ title,
25
+ closeOnClickOutside,
26
+ }: {
27
+ children: ReactNode;
28
+ closeOnClickOutside: boolean;
29
+ onClose: () => void;
30
+ title: string;
31
+ }) {
32
+ const modalRef = useRef<HTMLDivElement>(null);
33
+ const [fixed, setFixed] = React.useState(true);
34
+ const [disabled, setDisabled] = React.useState(true);
35
+ const [bounds, setBounds] = React.useState({
36
+ bottom: 0,
37
+ left: 0,
38
+ right: 0,
39
+ top: 0,
40
+ });
41
+ useEffect(() => {
42
+ if (modalRef.current !== null) {
43
+ modalRef.current.focus();
44
+ }
45
+ }, []);
46
+ const onStart = React.useCallback(
47
+ (event: DraggableEvent, uiData: DraggableData) => {
48
+ const {clientWidth, clientHeight} = window.document.documentElement;
49
+ const targetRect = modalRef.current?.getBoundingClientRect();
50
+ if (!targetRect) {
51
+ return;
52
+ }
53
+ setBounds({
54
+ bottom: clientHeight - (targetRect.bottom - uiData.y),
55
+ left: -targetRect.left + uiData.x,
56
+ right: clientWidth - (targetRect.right - uiData.x),
57
+ top: -targetRect.top + uiData.y,
58
+ });
59
+ },
60
+ [],
61
+ );
62
+ useEffect(() => {
63
+ let modalOverlayElement: HTMLElement | null = null;
64
+ const handler = (event: KeyboardEvent) => {
65
+ if (event.key === 'Escape') {
66
+ onClose();
67
+ }
68
+ };
69
+ const clickOutsideHandler = (event: MouseEvent) => {
70
+ const target = event.target;
71
+ if (
72
+ modalRef.current !== null &&
73
+ isDOMNode(target) &&
74
+ !modalRef.current.contains(target) &&
75
+ closeOnClickOutside
76
+ ) {
77
+ onClose();
78
+ }
79
+ };
80
+ const modelElement = modalRef.current;
81
+ if (modelElement !== null) {
82
+ modalOverlayElement = modelElement.parentElement;
83
+ if (modalOverlayElement !== null) {
84
+ modalOverlayElement.addEventListener('click', clickOutsideHandler);
85
+ }
86
+ }
87
+
88
+ window.addEventListener('keydown', handler);
89
+
90
+ return () => {
91
+ window.removeEventListener('keydown', handler);
92
+ if (modalOverlayElement !== null) {
93
+ modalOverlayElement?.removeEventListener('click', clickOutsideHandler);
94
+ }
95
+ };
96
+ }, [closeOnClickOutside, onClose]);
97
+
98
+ const onDialog = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
99
+ const target = e.target;
100
+ if (
101
+ target instanceof HTMLDivElement &&
102
+ target.getAttribute('data-modal-root') &&
103
+ !fixed
104
+ ) {
105
+ onClose();
106
+ }
107
+ };
108
+
109
+ return (
110
+ <div
111
+ className={Styles.Modal__overlay}
112
+ data-modal-root={true}
113
+ role="presentation"
114
+ onClick={onDialog}>
115
+ <Draggable bounds={bounds} disabled={disabled} onStart={onStart}>
116
+ <div className={Styles.Modal__modal} tabIndex={-1} ref={modalRef}>
117
+ <div
118
+ className={Styles.Modal__titleBox}
119
+ onMouseEnter={() => {
120
+ if (disabled) {
121
+ setDisabled(false);
122
+ }
123
+ }}
124
+ onMouseLeave={() => {
125
+ setDisabled(true);
126
+ }}>
127
+ {' '}
128
+ <span className={Styles.Modal__title}>{title}</span>
129
+ <button
130
+ className={Styles.Modal__closeButton}
131
+ aria-label="Close modal"
132
+ type="button"
133
+ onClick={onClose}>
134
+ <svg
135
+ fill-rule="evenodd"
136
+ viewBox="64 64 896 896"
137
+ focusable="false"
138
+ data-icon="close"
139
+ width="1em"
140
+ height="1em"
141
+ fill="currentColor"
142
+ aria-hidden="true">
143
+ <path d="M799.86 166.31c.02 0 .04.02.08.06l57.69 57.7c.04.03.05.05.06.08a.12.12 0 010 .06c0 .03-.02.05-.06.09L569.93 512l287.7 287.7c.04.04.05.06.06.09a.12.12 0 010 .07c0 .02-.02.04-.06.08l-57.7 57.69c-.03.04-.05.05-.07.06a.12.12 0 01-.07 0c-.03 0-.05-.02-.09-.06L512 569.93l-287.7 287.7c-.04.04-.06.05-.09.06a.12.12 0 01-.07 0c-.02 0-.04-.02-.08-.06l-57.69-57.7c-.04-.03-.05-.05-.06-.07a.12.12 0 010-.07c0-.03.02-.05.06-.09L454.07 512l-287.7-287.7c-.04-.04-.05-.06-.06-.09a.12.12 0 010-.07c0-.02.02-.04.06-.08l57.7-57.69c.03-.04.05-.05.07-.06a.12.12 0 01.07 0c.03 0 .05.02.09.06L512 454.07l287.7-287.7c.04-.04.06-.05.09-.06a.12.12 0 01.07 0z" />
144
+ </svg>
145
+ </button>
146
+ </div>
147
+ <div className={Styles.Modal__contentBox}>
148
+ <div className={Styles.Modal__content}>{children}</div>
149
+ </div>
150
+ </div>
151
+ </Draggable>
152
+ </div>
153
+ );
154
+ }
155
+
156
+ export default function Modal({
157
+ onClose,
158
+ children,
159
+ title,
160
+ closeOnClickOutside = false,
161
+ }: {
162
+ children: ReactNode;
163
+ closeOnClickOutside?: boolean;
164
+ onClose: () => void;
165
+ title: string;
166
+ }): JSX.Element {
167
+ return createPortal(
168
+ <PortalImpl
169
+ onClose={onClose}
170
+ title={title}
171
+ closeOnClickOutside={closeOnClickOutside}>
172
+ {children}
173
+ </PortalImpl>,
174
+ document.body,
175
+ );
176
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ :root {
10
+ --select-border: #393939;
11
+ --select-focus: #101484;
12
+ --select-arrow: var(--select-border);
13
+ }
14
+
15
+ .select {
16
+ appearance: none;
17
+ -webkit-appearance: none;
18
+ -moz-appearance: none;
19
+ background-color: transparent;
20
+ border: none;
21
+ padding: 0 1em 0 0;
22
+ margin: 0;
23
+ font-family: inherit;
24
+ font-size: inherit;
25
+ cursor: inherit;
26
+ line-height: inherit;
27
+
28
+ z-index: 1;
29
+ outline: none;
30
+
31
+ min-width: 160px;
32
+ max-width: 290px;
33
+ border: 1px solid var(--select-border);
34
+ border-radius: 0.25em;
35
+ padding: 0.25em 0.5em;
36
+ font-size: 1rem;
37
+ cursor: pointer;
38
+ line-height: 1.4;
39
+ background: linear-gradient(to bottom, #ffffff 0%, #e5e5e5 100%);
40
+
41
+ }
package/src/Select.tsx ADDED
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import type {JSX} from 'react';
10
+
11
+ import * as React from 'react';
12
+
13
+ import Styles from './Input.module.less';
14
+ import SelectStyles from './Select.module.less';
15
+
16
+ type SelectIntrinsicProps = JSX.IntrinsicElements['select'];
17
+ interface SelectProps extends SelectIntrinsicProps {
18
+ label: string;
19
+ }
20
+
21
+ export default function Select({
22
+ children,
23
+ label,
24
+ className,
25
+ ...other
26
+ }: SelectProps): JSX.Element {
27
+ return (
28
+ <div className={Styles.Input__wrapper}>
29
+ <label style={{marginTop: '-1em'}} className={Styles.Input__label}>
30
+ {label}
31
+ </label>
32
+ <select {...other} className={className || SelectStyles.select}>
33
+ {children}
34
+ </select>
35
+ </div>
36
+ );
37
+ }
@@ -0,0 +1,67 @@
1
+ .skeleton {
2
+ display: inline-block;
3
+ :global {
4
+ div {
5
+ display: inline-block;
6
+ }
7
+ div[data-type='avatar'] {
8
+ width: 60px;
9
+ height: 60px;
10
+ border-radius: 50%;
11
+ background: #e0e0e0;
12
+ }
13
+
14
+ div[data-type='block'] {
15
+ height: 15px;
16
+ background: #e0e0e0;
17
+ border-radius: 2px;
18
+ position: relative;
19
+ overflow: hidden;
20
+ }
21
+
22
+ div[data-type='rect'] {
23
+ height: 120px;
24
+ border-radius: 2px;
25
+ background: #e0e0e0;
26
+ }
27
+
28
+ div[data-type='title'] {
29
+ height: 25px;
30
+ background: #e0e0e0;
31
+ border-radius: 2px;
32
+ }
33
+
34
+ div[data-type='text'] {
35
+ height: 16px;
36
+ background: #e0e0e0;
37
+ border-radius: 2px;
38
+ }
39
+
40
+ div[data-type='button'] {
41
+ height: 36px;
42
+ width: 100px;
43
+ border-radius: 50%;
44
+ background: #e0e0e0;
45
+ }
46
+
47
+ div.animated :local {
48
+ background: linear-gradient(
49
+ 90deg,
50
+ #f0f0f0 25%,
51
+ #e0e0e0 50%,
52
+ #f0f0f0 75%
53
+ );
54
+ background-size: 200% 100%;
55
+ animation: shimmer 1.5s infinite;
56
+ @keyframes shimmer {
57
+ from {
58
+ background-position: -200% 0;
59
+ }
60
+
61
+ to {
62
+ background-position: 200% 0;
63
+ }
64
+ }
65
+ }
66
+ }
67
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+ import Styles from './Skeleton.module.less';
9
+
10
+ interface SkeletonProps extends BaseComponentProps {
11
+ type: 'avatar' | 'block' | 'text' | 'button' | 'rect' | 'title';
12
+ }
13
+
14
+ export default function Skeleton({
15
+ className,
16
+ style,
17
+ type,
18
+ }: SkeletonProps): JSX.Element {
19
+ return (
20
+ <div className={Styles.skeleton}>
21
+ <div
22
+ data-type={type}
23
+ className={`${className ?? ''} ${Styles.skeleton} animated`}
24
+ style={style}
25
+ />
26
+ </div>
27
+ );
28
+ }
package/src/Switch.tsx ADDED
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import type {JSX} from 'react';
10
+
11
+ import * as React from 'react';
12
+ import {useMemo} from 'react';
13
+
14
+ export default function Switch({
15
+ checked,
16
+ onClick,
17
+ text,
18
+ id,
19
+ }: Readonly<{
20
+ checked: boolean;
21
+ id?: string;
22
+ onClick: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
23
+ text: string;
24
+ }>): JSX.Element {
25
+ const buttonId = useMemo(() => 'id_' + Math.floor(Math.random() * 10000), []);
26
+ return (
27
+ <div className="switch" id={id}>
28
+ <label htmlFor={buttonId}>{text}</label>
29
+ <button
30
+ role="switch"
31
+ aria-checked={checked}
32
+ id={buttonId}
33
+ onClick={onClick}>
34
+ <span />
35
+ </button>
36
+ </div>
37
+ );
38
+ }