@dxos/react-ui-editor 0.6.8-main.046e6cf → 0.6.8-staging.77f93a3
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/lib/browser/index.mjs +290 -293
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/TextEditor.stories.d.ts +5 -2
- package/dist/types/src/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/defaults.d.ts +1 -1
- package/dist/types/src/defaults.d.ts.map +1 -1
- package/dist/types/src/extensions/doc.d.ts +3 -0
- package/dist/types/src/extensions/doc.d.ts.map +1 -1
- package/dist/types/src/extensions/factories.d.ts.map +1 -1
- package/dist/types/src/extensions/folding.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/formatting.d.ts +1 -1
- package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/highlight.d.ts +2 -1
- package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/link-paste.d.ts +3 -0
- package/dist/types/src/extensions/markdown/link-paste.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/link.d.ts +3 -1
- package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
- package/dist/types/src/extensions/state.d.ts +14 -14
- package/dist/types/src/extensions/state.d.ts.map +1 -1
- package/dist/types/src/extensions/util/react.d.ts +1 -1
- package/dist/types/src/extensions/util/react.d.ts.map +1 -1
- package/dist/types/src/hooks/useTextEditor.d.ts +5 -3
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
- package/dist/types/src/styles/markdown.d.ts +7 -17
- package/dist/types/src/styles/markdown.d.ts.map +1 -1
- package/dist/types/src/styles/theme.d.ts +2 -1
- package/dist/types/src/styles/theme.d.ts.map +1 -1
- package/dist/types/src/styles/tokens.d.ts +5 -7
- package/dist/types/src/styles/tokens.d.ts.map +1 -1
- package/package.json +24 -24
- package/src/TextEditor.stories.tsx +40 -27
- package/src/defaults.ts +2 -1
- package/src/extensions/doc.ts +3 -0
- package/src/extensions/factories.ts +3 -2
- package/src/extensions/folding.tsx +5 -7
- package/src/extensions/markdown/bundle.ts +1 -3
- package/src/extensions/markdown/decorate.ts +29 -23
- package/src/extensions/markdown/formatting.ts +3 -1
- package/src/extensions/markdown/highlight.ts +33 -19
- package/src/extensions/markdown/link-paste.ts +3 -0
- package/src/extensions/state.ts +41 -35
- package/src/extensions/util/react.tsx +3 -4
- package/src/hooks/useTextEditor.ts +24 -29
- package/src/styles/markdown.ts +17 -40
- package/src/styles/theme.ts +42 -49
- package/src/styles/tokens.ts +9 -7
|
@@ -7,6 +7,9 @@ import { type EditorState, Transaction } from '@codemirror/state';
|
|
|
7
7
|
import { ViewPlugin, type ViewUpdate, type PluginValue } from '@codemirror/view';
|
|
8
8
|
import { type SyntaxNode } from '@lezer/common';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Formats pasted URLs as markdown links and images.
|
|
12
|
+
*/
|
|
10
13
|
export const linkPastePlugin = ViewPlugin.fromClass(
|
|
11
14
|
class implements PluginValue {
|
|
12
15
|
update(update: ViewUpdate) {
|
package/src/extensions/state.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { type Extension, Transaction } from '@codemirror/state';
|
|
5
|
+
import { type Extension, Transaction, type TransactionSpec } from '@codemirror/state';
|
|
6
6
|
import { EditorView, keymap } from '@codemirror/view';
|
|
7
7
|
|
|
8
8
|
import { debounce } from '@dxos/async';
|
|
@@ -11,76 +11,82 @@ import { isNotFalsy } from '@dxos/util';
|
|
|
11
11
|
|
|
12
12
|
import { documentId } from './doc';
|
|
13
13
|
|
|
14
|
-
const
|
|
14
|
+
const stateRestoreAnnotation = 'dxos.org/cm/state-restore';
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
16
|
+
export type EditorSelection = {
|
|
17
|
+
anchor: number;
|
|
18
|
+
head?: number;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type EditorSelectionState = {
|
|
22
|
+
scrollTo?: number;
|
|
23
|
+
selection?: EditorSelection;
|
|
25
24
|
};
|
|
26
25
|
|
|
27
|
-
export type
|
|
28
|
-
setState: (id: string, state:
|
|
29
|
-
getState: (id: string) =>
|
|
26
|
+
export type EditorStateOptions = {
|
|
27
|
+
setState: (id: string, state: EditorSelectionState) => void;
|
|
28
|
+
getState: (id: string) => EditorSelectionState | undefined;
|
|
30
29
|
};
|
|
31
30
|
|
|
32
31
|
const keyPrefix = 'dxos.org/react-ui-editor/state';
|
|
33
|
-
export const localStorageStateStoreAdapter:
|
|
34
|
-
setState: (id, state) => {
|
|
35
|
-
invariant(id);
|
|
36
|
-
localStorage.setItem(`${keyPrefix}/${id}`, JSON.stringify(state));
|
|
37
|
-
},
|
|
32
|
+
export const localStorageStateStoreAdapter: EditorStateOptions = {
|
|
38
33
|
getState: (id) => {
|
|
39
34
|
invariant(id);
|
|
40
35
|
const state = localStorage.getItem(`${keyPrefix}/${id}`);
|
|
41
36
|
return state ? JSON.parse(state) : undefined;
|
|
42
37
|
},
|
|
38
|
+
|
|
39
|
+
setState: (id, state) => {
|
|
40
|
+
invariant(id);
|
|
41
|
+
localStorage.setItem(`${keyPrefix}/${id}`, JSON.stringify(state));
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const createEditorStateTransaction = ({ scrollTo, selection }: EditorSelectionState): TransactionSpec => {
|
|
46
|
+
return {
|
|
47
|
+
selection,
|
|
48
|
+
scrollIntoView: !scrollTo,
|
|
49
|
+
effects: scrollTo ? EditorView.scrollIntoView(scrollTo, { yMargin: 96 }) : undefined,
|
|
50
|
+
annotations: Transaction.userEvent.of(stateRestoreAnnotation),
|
|
51
|
+
};
|
|
43
52
|
};
|
|
44
53
|
|
|
45
54
|
/**
|
|
46
55
|
* Track scrolling and selection state to be restored when switching to document.
|
|
47
56
|
*/
|
|
48
|
-
export const state = ({ getState, setState }: Partial<
|
|
57
|
+
export const state = ({ getState, setState }: Partial<EditorStateOptions> = {}): Extension => {
|
|
49
58
|
const setStateDebounced = debounce(setState!, 1_000);
|
|
50
59
|
|
|
51
60
|
return [
|
|
52
61
|
// TODO(burdon): Track scrolling (currently only updates when cursor moves).
|
|
53
|
-
EditorView.
|
|
54
|
-
|
|
62
|
+
// EditorView.domEventHandlers({
|
|
63
|
+
// scroll: (event) => {
|
|
64
|
+
// setStateDebounced(id, {});
|
|
65
|
+
// },
|
|
66
|
+
// }),
|
|
67
|
+
EditorView.updateListener.of(({ view, transactions }) => {
|
|
55
68
|
const id = view.state.facet(documentId);
|
|
56
|
-
if (!id || transactions.some((tr) => tr.isUserEvent(
|
|
69
|
+
if (!id || transactions.some((tr) => tr.isUserEvent(stateRestoreAnnotation))) {
|
|
57
70
|
return;
|
|
58
71
|
}
|
|
59
72
|
|
|
60
73
|
if (setState) {
|
|
61
|
-
const {
|
|
62
|
-
const pos = view.posAtCoords({ x: 0, y:
|
|
74
|
+
const { scrollTop } = view.scrollDOM;
|
|
75
|
+
const pos = view.posAtCoords({ x: 0, y: scrollTop });
|
|
63
76
|
if (pos !== null) {
|
|
64
77
|
const { anchor, head } = view.state.selection.main;
|
|
65
|
-
setStateDebounced(id, {
|
|
66
|
-
scrollTo: { from: pos, yMargin: 0 },
|
|
67
|
-
selection: { anchor, head },
|
|
68
|
-
});
|
|
78
|
+
setStateDebounced(id, { scrollTo: pos, selection: { anchor, head } });
|
|
69
79
|
}
|
|
70
80
|
}
|
|
71
81
|
}),
|
|
72
82
|
getState &&
|
|
73
83
|
keymap.of([
|
|
74
84
|
{
|
|
75
|
-
key: 'ctrl-r', // TODO(burdon): Setting to jump back to
|
|
85
|
+
key: 'ctrl-r', // TODO(burdon): Setting to jump back to selection.
|
|
76
86
|
run: (view) => {
|
|
77
87
|
const state = getState(view.state.facet(documentId));
|
|
78
88
|
if (state) {
|
|
79
|
-
view.dispatch(
|
|
80
|
-
effects: EditorView.scrollIntoView(state.scrollTo.from, { yMargin: 0 }),
|
|
81
|
-
selection: state.selection,
|
|
82
|
-
annotations: Transaction.userEvent.of(scrollAnnotation),
|
|
83
|
-
});
|
|
89
|
+
view.dispatch(createEditorStateTransaction(state));
|
|
84
90
|
}
|
|
85
91
|
return true;
|
|
86
92
|
},
|
|
@@ -8,8 +8,7 @@ import { createRoot } from 'react-dom/client';
|
|
|
8
8
|
import { ThemeProvider } from '@dxos/react-ui';
|
|
9
9
|
import { defaultTx } from '@dxos/react-ui-theme';
|
|
10
10
|
|
|
11
|
-
export const renderRoot = (node: ReactNode) => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return el;
|
|
11
|
+
export const renderRoot = (root: HTMLElement, node: ReactNode): HTMLElement => {
|
|
12
|
+
createRoot(root).render(<ThemeProvider tx={defaultTx}>{node}</ThemeProvider>);
|
|
13
|
+
return root;
|
|
15
14
|
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { EditorState, type EditorStateConfig
|
|
5
|
+
import { EditorState, type EditorStateConfig } from '@codemirror/state';
|
|
6
6
|
import { EditorView } from '@codemirror/view';
|
|
7
7
|
import { useFocusableGroup } from '@fluentui/react-tabster';
|
|
8
8
|
import {
|
|
@@ -17,10 +17,9 @@ import {
|
|
|
17
17
|
} from 'react';
|
|
18
18
|
|
|
19
19
|
import { log } from '@dxos/log';
|
|
20
|
-
import { useDefaultValue } from '@dxos/react-ui';
|
|
21
20
|
import { isNotFalsy, type MaybeFunction } from '@dxos/util';
|
|
22
21
|
|
|
23
|
-
import { documentId, editorInputMode } from '../extensions';
|
|
22
|
+
import { createEditorStateTransaction, documentId, editorInputMode, type EditorSelection } from '../extensions';
|
|
24
23
|
import { logChanges } from '../util';
|
|
25
24
|
|
|
26
25
|
export type UseTextEditor = {
|
|
@@ -41,12 +40,13 @@ export type CursorInfo = {
|
|
|
41
40
|
after?: string;
|
|
42
41
|
};
|
|
43
42
|
|
|
44
|
-
export type UseTextEditorProps = Pick<EditorStateConfig, '
|
|
43
|
+
export type UseTextEditorProps = Pick<EditorStateConfig, 'extensions'> & {
|
|
45
44
|
id?: string;
|
|
46
45
|
initialValue?: string;
|
|
47
46
|
className?: string;
|
|
48
47
|
autoFocus?: boolean;
|
|
49
|
-
scrollTo?:
|
|
48
|
+
scrollTo?: number;
|
|
49
|
+
selection?: EditorSelection;
|
|
50
50
|
moveToEndOfLine?: boolean;
|
|
51
51
|
debug?: boolean;
|
|
52
52
|
};
|
|
@@ -60,22 +60,14 @@ export const useTextEditor = (
|
|
|
60
60
|
props: MaybeFunction<UseTextEditorProps> = {},
|
|
61
61
|
deps: DependencyList = [],
|
|
62
62
|
): UseTextEditor => {
|
|
63
|
-
const {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
extensions,
|
|
68
|
-
autoFocus,
|
|
69
|
-
scrollTo: _scrollTo,
|
|
70
|
-
moveToEndOfLine,
|
|
71
|
-
debug,
|
|
72
|
-
} = useMemo<UseTextEditorProps>(() => {
|
|
73
|
-
return typeof props === 'function' ? props() : props;
|
|
74
|
-
}, deps ?? []);
|
|
63
|
+
const { id, initialValue, extensions, autoFocus, scrollTo, selection, moveToEndOfLine, debug } =
|
|
64
|
+
useMemo<UseTextEditorProps>(() => {
|
|
65
|
+
return typeof props === 'function' ? props() : props;
|
|
66
|
+
}, deps ?? []);
|
|
75
67
|
|
|
76
68
|
// NOTE: Increments by 2 in strict mode.
|
|
77
69
|
const [instanceId] = useState(() => `text-editor-${++instanceCount}`);
|
|
78
|
-
|
|
70
|
+
// Callback once view is created.
|
|
79
71
|
const onUpdate = useRef<() => void>();
|
|
80
72
|
const [view, setView] = useState<EditorView>();
|
|
81
73
|
const parentRef = useRef<HTMLDivElement>(null);
|
|
@@ -85,8 +77,12 @@ export const useTextEditor = (
|
|
|
85
77
|
if (parentRef.current) {
|
|
86
78
|
log('create', { id, instanceId, doc: initialValue?.length ?? 0 });
|
|
87
79
|
|
|
88
|
-
let initialSelection
|
|
89
|
-
if (
|
|
80
|
+
let initialSelection;
|
|
81
|
+
if (selection?.anchor && initialValue?.length) {
|
|
82
|
+
if (selection.anchor <= initialValue.length && (selection?.head ?? 0) <= initialValue.length) {
|
|
83
|
+
initialSelection = selection;
|
|
84
|
+
}
|
|
85
|
+
} else if (moveToEndOfLine && selection === undefined) {
|
|
90
86
|
const index = initialValue?.indexOf('\n');
|
|
91
87
|
const anchor = !index || index === -1 ? 0 : index;
|
|
92
88
|
initialSelection = { anchor };
|
|
@@ -105,7 +101,9 @@ export const useTextEditor = (
|
|
|
105
101
|
}),
|
|
106
102
|
extensions,
|
|
107
103
|
EditorView.updateListener.of(() => {
|
|
108
|
-
|
|
104
|
+
setTimeout(() => {
|
|
105
|
+
onUpdate.current?.();
|
|
106
|
+
});
|
|
109
107
|
}),
|
|
110
108
|
].filter(isNotFalsy),
|
|
111
109
|
});
|
|
@@ -113,7 +111,6 @@ export const useTextEditor = (
|
|
|
113
111
|
// https://codemirror.net/docs/ref/#view.EditorViewConfig
|
|
114
112
|
view = new EditorView({
|
|
115
113
|
parent: parentRef.current,
|
|
116
|
-
scrollTo,
|
|
117
114
|
selection: initialSelection,
|
|
118
115
|
state,
|
|
119
116
|
// NOTE: Uncomment to debug/monitor all transactions.
|
|
@@ -143,13 +140,11 @@ export const useTextEditor = (
|
|
|
143
140
|
|
|
144
141
|
useEffect(() => {
|
|
145
142
|
if (view) {
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
onUpdate.current =
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
};
|
|
152
|
-
}
|
|
143
|
+
// NOTE: Set selection after first update (since content may rerender on focus).
|
|
144
|
+
onUpdate.current = () => {
|
|
145
|
+
onUpdate.current = undefined;
|
|
146
|
+
view.dispatch(createEditorStateTransaction({ scrollTo, selection }));
|
|
147
|
+
};
|
|
153
148
|
|
|
154
149
|
// Remove tabster attribute (rely on custom keymap).
|
|
155
150
|
if (view.state.facet(editorInputMode).noTabster) {
|
package/src/styles/markdown.ts
CHANGED
|
@@ -6,48 +6,25 @@ import { mx } from '@dxos/react-ui-theme';
|
|
|
6
6
|
|
|
7
7
|
export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
|
8
8
|
|
|
9
|
-
// TODO(burdon): Better way to align vertically than negative margin? Font-specific?
|
|
10
9
|
// https://tailwindcss.com/docs/font-weight
|
|
11
10
|
const headings: Record<HeadingLevel, string> = {
|
|
12
|
-
1: '
|
|
13
|
-
2: '
|
|
14
|
-
3: '
|
|
15
|
-
4: '
|
|
16
|
-
5: '
|
|
17
|
-
6: '
|
|
11
|
+
1: 'text-4xl',
|
|
12
|
+
2: 'text-3xl',
|
|
13
|
+
3: 'text-2xl',
|
|
14
|
+
4: 'text-xl',
|
|
15
|
+
5: 'text-lg',
|
|
16
|
+
6: 'text-md',
|
|
18
17
|
};
|
|
19
18
|
|
|
20
|
-
// TODO(burdon):
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
// TODO(burdon): Define theme as facet (used in multiple extensions).
|
|
20
|
+
// TODO(burdon): Organize theme styles for widgets.
|
|
21
|
+
export const theme = {
|
|
22
|
+
mark: 'opacity-50',
|
|
23
|
+
code: 'font-mono !no-underline text-neutral-700 dark:text-neutral-300',
|
|
24
|
+
codeMark: 'font-mono text-primary-500',
|
|
25
|
+
// TODO(burdon): Replace with widget.
|
|
26
|
+
blockquote: 'pl-1 mr-1 border-is-4 border-orange-500 dark:border-orange-500 dark:text-neutral-500',
|
|
27
|
+
heading: (level: HeadingLevel) => {
|
|
28
|
+
return mx(headings[level], 'dark:text-primary-400');
|
|
29
|
+
},
|
|
23
30
|
};
|
|
24
|
-
|
|
25
|
-
export const text = 'text-neutral-800 dark:text-neutral-200';
|
|
26
|
-
export const light = 'text-neutral-200 dark:text-neutral-800';
|
|
27
|
-
|
|
28
|
-
export const mark = mx('!font-normal !no-underline !text-inherit opacity-40', light);
|
|
29
|
-
|
|
30
|
-
export const paragraph = 'mlb-1';
|
|
31
|
-
|
|
32
|
-
export const bold = 'font-bold';
|
|
33
|
-
export const italic = 'italic';
|
|
34
|
-
export const strikethrough = 'line-through';
|
|
35
|
-
|
|
36
|
-
export const code = 'font-mono !no-underline text-neutral-700 dark:text-neutral-300';
|
|
37
|
-
export const codeMark = 'font-mono text-primary-500';
|
|
38
|
-
export const codeBlock = 'mlb-2 font-mono bg-neutral-500/10 p-3 rounded';
|
|
39
|
-
|
|
40
|
-
export const inlineUrl = mx(code, 'px-1');
|
|
41
|
-
|
|
42
|
-
export const blockquote = mx('pl-1 mr-1 border-is-4 border-orange-500 dark:border-orange-500 text-transparent');
|
|
43
|
-
|
|
44
|
-
export const horizontalRule =
|
|
45
|
-
'flex mlb-4 border-b text-neutral-100 dark:text-neutral-900 border-neutral-200 dark:border-neutral-800';
|
|
46
|
-
|
|
47
|
-
// TODO(thure): Tailwind was not seeing `[&>li:before]:content-["•"]` as a utility class, but it would work if instead of `"•"` it was `"X"`… why?
|
|
48
|
-
export const unorderedList =
|
|
49
|
-
'mlb-2 grid grid-cols-[min-content_1fr] [&>li:before]:content-[attr(marker)] [&>li:before]:mlb-1 [&>li:before]:mie-2';
|
|
50
|
-
export const orderedList =
|
|
51
|
-
'mlb-2 grid grid-cols-[min-content_1fr] [&>li:before]:content-[counters(section,_".")_"._"] [counter-reset:section] [&>li:before]:mlb-1';
|
|
52
|
-
|
|
53
|
-
export const listItem = 'contents before:[counter-increment:section]';
|
package/src/styles/theme.ts
CHANGED
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import { type StyleSpec } from 'style-mod';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { getToken } from './tokens';
|
|
8
8
|
|
|
9
|
+
export type ThemeStyles = Record<string, StyleSpec>;
|
|
10
|
+
|
|
11
|
+
// TODO(burdon): Factor out theme.
|
|
9
12
|
// TODO(burdon): Can we use @apply and import css file?
|
|
10
13
|
// https://tailwindcss.com/docs/reusing-styles#extracting-classes-with-apply?
|
|
11
14
|
|
|
@@ -52,8 +55,6 @@ export const defaultTheme: ThemeStyles = {
|
|
|
52
55
|
// NOTE: See https://codemirror.net/docs/guide (DOM Structure).
|
|
53
56
|
'.cm-scroller': {
|
|
54
57
|
overflowY: 'auto',
|
|
55
|
-
fontFamily: get(tokens, 'fontFamily.body', []).join(','),
|
|
56
|
-
lineHeight: 1.5,
|
|
57
58
|
},
|
|
58
59
|
|
|
59
60
|
// Content.
|
|
@@ -61,12 +62,14 @@ export const defaultTheme: ThemeStyles = {
|
|
|
61
62
|
padding: 'unset',
|
|
62
63
|
// NOTE: Base font size (otherwise defined by HTML tag, which might be different for storybook).
|
|
63
64
|
fontSize: '16px',
|
|
65
|
+
fontFamily: getToken('fontFamily.body'),
|
|
66
|
+
lineHeight: 1.5,
|
|
64
67
|
},
|
|
65
68
|
'&light .cm-content': {
|
|
66
|
-
color:
|
|
69
|
+
color: getToken('extend.semanticColors.base.fg.light', 'black'),
|
|
67
70
|
},
|
|
68
71
|
'&dark .cm-content': {
|
|
69
|
-
color:
|
|
72
|
+
color: getToken('extend.semanticColors.base.fg.dark', 'white'),
|
|
70
73
|
},
|
|
71
74
|
|
|
72
75
|
//
|
|
@@ -79,10 +82,10 @@ export const defaultTheme: ThemeStyles = {
|
|
|
79
82
|
borderLeft: '2px solid white',
|
|
80
83
|
},
|
|
81
84
|
'&light .cm-placeholder': {
|
|
82
|
-
color:
|
|
85
|
+
color: getToken('extend.semanticColors.description.light', 'rgba(0,0,0,.2)'),
|
|
83
86
|
},
|
|
84
87
|
'&dark .cm-placeholder': {
|
|
85
|
-
color:
|
|
88
|
+
color: getToken('extend.semanticColors.description.dark', 'rgba(255,255,255,.2)'),
|
|
86
89
|
},
|
|
87
90
|
|
|
88
91
|
//
|
|
@@ -107,16 +110,16 @@ export const defaultTheme: ThemeStyles = {
|
|
|
107
110
|
//
|
|
108
111
|
|
|
109
112
|
'&light .cm-selectionBackground': {
|
|
110
|
-
background:
|
|
113
|
+
background: getToken('extend.colors.primary.100'),
|
|
111
114
|
},
|
|
112
115
|
'&light.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground': {
|
|
113
|
-
background:
|
|
116
|
+
background: getToken('extend.colors.primary.200'),
|
|
114
117
|
},
|
|
115
118
|
'&dark .cm-selectionBackground': {
|
|
116
|
-
background:
|
|
119
|
+
background: getToken('extend.colors.primary.700'),
|
|
117
120
|
},
|
|
118
121
|
'&dark.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground': {
|
|
119
|
-
background:
|
|
122
|
+
background: getToken('extend.colors.primary.600'),
|
|
120
123
|
},
|
|
121
124
|
|
|
122
125
|
//
|
|
@@ -124,22 +127,27 @@ export const defaultTheme: ThemeStyles = {
|
|
|
124
127
|
//
|
|
125
128
|
|
|
126
129
|
'&light .cm-searchMatch': {
|
|
127
|
-
backgroundColor:
|
|
130
|
+
backgroundColor: getToken('extend.colors.yellow.100'),
|
|
128
131
|
},
|
|
129
132
|
'&dark .cm-searchMatch': {
|
|
130
|
-
backgroundColor:
|
|
133
|
+
backgroundColor: getToken('extend.colors.yellow.700'),
|
|
131
134
|
},
|
|
132
135
|
|
|
133
136
|
//
|
|
134
137
|
// link
|
|
135
138
|
//
|
|
136
139
|
'.cm-link': {
|
|
137
|
-
color: get(tokens, 'extend.colors.primary.500'),
|
|
138
140
|
textDecorationLine: 'underline',
|
|
139
141
|
textDecorationThickness: '1px',
|
|
140
142
|
textUnderlineOffset: '2px',
|
|
141
143
|
borderRadius: '.125rem',
|
|
142
|
-
fontFamily:
|
|
144
|
+
fontFamily: getToken('fontFamily.body'),
|
|
145
|
+
},
|
|
146
|
+
'&light .cm-link > span': {
|
|
147
|
+
color: getToken('extend.colors.primary.600'),
|
|
148
|
+
},
|
|
149
|
+
'&dark .cm-link > span': {
|
|
150
|
+
color: getToken('extend.colors.primary.400'),
|
|
143
151
|
},
|
|
144
152
|
|
|
145
153
|
//
|
|
@@ -147,10 +155,10 @@ export const defaultTheme: ThemeStyles = {
|
|
|
147
155
|
//
|
|
148
156
|
'.cm-tooltip': {},
|
|
149
157
|
'&light .cm-tooltip': {
|
|
150
|
-
background: `${
|
|
158
|
+
background: `${getToken('extend.colors.neutral.100')} !important`,
|
|
151
159
|
},
|
|
152
160
|
'&dark .cm-tooltip': {
|
|
153
|
-
background: `${
|
|
161
|
+
background: `${getToken('extend.colors.neutral.900')} !important`,
|
|
154
162
|
},
|
|
155
163
|
'.cm-tooltip-below': {},
|
|
156
164
|
|
|
@@ -167,14 +175,13 @@ export const defaultTheme: ThemeStyles = {
|
|
|
167
175
|
},
|
|
168
176
|
'.cm-tooltip.cm-tooltip-autocomplete > ul > li': {},
|
|
169
177
|
'.cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]': {},
|
|
170
|
-
// TODO(burdon): Can we add a class prefix to avoid adding !important?
|
|
171
178
|
'.cm-tooltip.cm-tooltip-autocomplete > ul > completion-section': {
|
|
172
179
|
paddingLeft: '4px !important',
|
|
173
180
|
borderBottom: 'none !important',
|
|
174
|
-
color:
|
|
181
|
+
color: getToken('extend.colors.primary.500'),
|
|
175
182
|
},
|
|
176
183
|
'.cm-tooltip.cm-completionInfo': {
|
|
177
|
-
border:
|
|
184
|
+
border: getToken('extend.colors.neutral.500'),
|
|
178
185
|
width: '360px !important',
|
|
179
186
|
margin: '-10px 1px 0 1px',
|
|
180
187
|
padding: '8px !important',
|
|
@@ -183,7 +190,7 @@ export const defaultTheme: ThemeStyles = {
|
|
|
183
190
|
display: 'none',
|
|
184
191
|
},
|
|
185
192
|
'.cm-completionLabel': {
|
|
186
|
-
fontFamily:
|
|
193
|
+
fontFamily: getToken('fontFamily.body'),
|
|
187
194
|
},
|
|
188
195
|
'.cm-completionMatchedText': {
|
|
189
196
|
textDecoration: 'none !important',
|
|
@@ -194,14 +201,14 @@ export const defaultTheme: ThemeStyles = {
|
|
|
194
201
|
// table
|
|
195
202
|
//
|
|
196
203
|
'.cm-table *': {
|
|
197
|
-
fontFamily: `${
|
|
204
|
+
fontFamily: `${getToken('fontFamily.mono')} !important`,
|
|
198
205
|
textDecoration: 'none !important',
|
|
199
206
|
},
|
|
200
207
|
'.cm-table-head': {
|
|
201
208
|
padding: '2px 16px 2px 0px',
|
|
202
209
|
textAlign: 'left',
|
|
203
|
-
borderBottom: `1px solid ${
|
|
204
|
-
color:
|
|
210
|
+
borderBottom: `1px solid ${getToken('extend.colors.primary.500')}`,
|
|
211
|
+
color: getToken('extend.colors.neutral.500'),
|
|
205
212
|
},
|
|
206
213
|
'.cm-table-cell': {
|
|
207
214
|
padding: '2px 16px 2px 0px',
|
|
@@ -220,20 +227,6 @@ export const defaultTheme: ThemeStyles = {
|
|
|
220
227
|
borderBottom: '0.5rem solid transparent',
|
|
221
228
|
},
|
|
222
229
|
|
|
223
|
-
//
|
|
224
|
-
// font size
|
|
225
|
-
// TODO(thure): This appears to be the best or only way to set selection caret heights,
|
|
226
|
-
// but it's far more verbose than it needs to be.
|
|
227
|
-
//
|
|
228
|
-
// ...Object.keys(get(tokens, 'extend.fontSize', {})).reduce((acc: Record<string, any>, fontSize) => {
|
|
229
|
-
// const height = get(tokens, ['extend', 'fontSize', fontSize, 1, 'lineHeight']);
|
|
230
|
-
//
|
|
231
|
-
// acc[`& .text-${fontSize} + .cm-ySelectionCaret`] = { height };
|
|
232
|
-
// acc[`& .text-${fontSize} + .cm-ySelection + .cm-ySelectionCaret`] = { height };
|
|
233
|
-
// acc[`& .text-${fontSize} + .cm-widgetBuffer + .cm-ySelectionCaret`] = { height };
|
|
234
|
-
// return acc;
|
|
235
|
-
// }, {}),
|
|
236
|
-
|
|
237
230
|
// TODO(burdon): Override vars --cm-background.
|
|
238
231
|
// https://www.npmjs.com/package/codemirror-theme-vars
|
|
239
232
|
|
|
@@ -262,20 +255,20 @@ export const defaultTheme: ThemeStyles = {
|
|
|
262
255
|
*/
|
|
263
256
|
'.cm-panels': {},
|
|
264
257
|
'.cm-panel': {
|
|
265
|
-
fontFamily:
|
|
258
|
+
fontFamily: getToken('fontFamily.body'),
|
|
266
259
|
},
|
|
267
260
|
'.cm-panel input[type=checkbox]': {
|
|
268
261
|
marginRight: '0.4rem !important',
|
|
269
262
|
},
|
|
270
263
|
'&light .cm-panel': {
|
|
271
|
-
background:
|
|
264
|
+
background: getToken('extend.colors.neutral.50'),
|
|
272
265
|
},
|
|
273
266
|
'&dark .cm-panel': {
|
|
274
|
-
background:
|
|
267
|
+
background: getToken('extend.colors.neutral.850'),
|
|
275
268
|
},
|
|
276
269
|
'.cm-button': {
|
|
277
270
|
margin: '4px',
|
|
278
|
-
fontFamily:
|
|
271
|
+
fontFamily: getToken('fontFamily.body'),
|
|
279
272
|
backgroundImage: 'none',
|
|
280
273
|
border: 'none',
|
|
281
274
|
'&:active': {
|
|
@@ -283,21 +276,21 @@ export const defaultTheme: ThemeStyles = {
|
|
|
283
276
|
},
|
|
284
277
|
},
|
|
285
278
|
'&light .cm-button': {
|
|
286
|
-
background:
|
|
279
|
+
background: getToken('extend.colors.neutral.100'),
|
|
287
280
|
'&:hover': {
|
|
288
|
-
background:
|
|
281
|
+
background: getToken('extend.colors.neutral.200'),
|
|
289
282
|
},
|
|
290
283
|
'&:active': {
|
|
291
|
-
background:
|
|
284
|
+
background: getToken('extend.colors.neutral.300'),
|
|
292
285
|
},
|
|
293
286
|
},
|
|
294
287
|
'&dark .cm-button': {
|
|
295
|
-
background:
|
|
288
|
+
background: getToken('extend.colors.neutral.800'),
|
|
296
289
|
'&:hover': {
|
|
297
|
-
background:
|
|
290
|
+
background: getToken('extend.colors.neutral.700'),
|
|
298
291
|
},
|
|
299
292
|
'&:active': {
|
|
300
|
-
background:
|
|
293
|
+
background: getToken('extend.colors.neutral.600'),
|
|
301
294
|
},
|
|
302
295
|
},
|
|
303
296
|
};
|
package/src/styles/tokens.ts
CHANGED
|
@@ -3,14 +3,16 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import get from 'lodash.get';
|
|
6
|
-
import type { StyleSpec } from 'style-mod';
|
|
7
6
|
|
|
8
7
|
import { tailwindConfig, type TailwindConfig } from '@dxos/react-ui-theme';
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
[selector: string]: StyleSpec;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export const tokens: TailwindConfig['theme'] = tailwindConfig({}).theme;
|
|
9
|
+
const tokens: TailwindConfig['theme'] = tailwindConfig({}).theme;
|
|
15
10
|
|
|
16
|
-
|
|
11
|
+
/**
|
|
12
|
+
* @deprecated
|
|
13
|
+
* Replace with CSS vars.
|
|
14
|
+
*/
|
|
15
|
+
export const getToken = (path: string, defaultValue?: string | string[]): string => {
|
|
16
|
+
const value = get(tokens, path, defaultValue);
|
|
17
|
+
return value?.toString() ?? '';
|
|
18
|
+
};
|