onchain-lexical-instance 0.0.2 → 0.0.4

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/package.json CHANGED
@@ -7,8 +7,12 @@
7
7
  "instance"
8
8
  ],
9
9
  "license": "MIT",
10
- "version": "0.0.2",
10
+ "version": "0.0.4",
11
11
  "types": "index.d.ts",
12
+ "files": [
13
+ "dist",
14
+ "src"
15
+ ],
12
16
  "dependencies": {
13
17
  "@lexical/code": "^0.30.0",
14
18
  "@lexical/hashtag": "^0.30.0",
@@ -19,10 +23,10 @@
19
23
  "@lexical/table": "^0.30.0",
20
24
  "@lexical/utils": "^0.30.0",
21
25
  "lexical": "0.30.0",
22
- "onchain-lexical-context": "^0.0.2",
23
- "onchain-lexical-ui": "^0.0.2",
24
- "onchain-utility": "^0.0.3",
25
- "onchain-lexical-markdown": "^0.0.2"
26
+ "onchain-lexical-context": "^0.0.3",
27
+ "onchain-lexical-markdown": "^0.0.3",
28
+ "onchain-lexical-ui": "^0.0.3",
29
+ "onchain-utility": "^0.0.4"
26
30
  },
27
31
  "sideEffects": false,
28
32
  "exports": {
@@ -11,9 +11,9 @@ import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
11
11
  import {useInstanceConfig} from 'onchain-lexical-context/instanceConfig';
12
12
  import {useSettings} from 'onchain-lexical-context/settings';
13
13
  import DropDown, {DropDownItem} from 'onchain-lexical-ui/DropDown';
14
- import {Icon} from 'onchain-lexical-ui/Icon';
14
+ import {Icon, StaticIcon} from 'onchain-lexical-ui/Icon';
15
15
  import {translateI18n} from 'onchain-utility';
16
- import {useCallback, useMemo} from 'react';
16
+ import {useCallback, useMemo, useState} from 'react';
17
17
 
18
18
  import {OPEN_CREATE_WINDOW} from '../const';
19
19
  import {Instance} from '../types';
@@ -26,8 +26,10 @@ const Bar = (props: {
26
26
  }): JSX.Element => {
27
27
  const {insNodeKey, instance} = props;
28
28
  const [editor] = useLexicalComposerContext();
29
- const {preview, selectedInstance, getInstanceIcon} = useInstanceConfig();
29
+ const {preview, selectedInstance, getInstanceIcon, components} =
30
+ useInstanceConfig();
30
31
  const {extra} = useSettings();
32
+ const [open, setOpen] = useState(false);
31
33
 
32
34
  // icon-front-link1
33
35
  const isSelected = useMemo(() => {
@@ -72,6 +74,12 @@ const Bar = (props: {
72
74
  return 'demand1';
73
75
  }, [instance]);
74
76
 
77
+ const Components = useMemo(() => {
78
+ return {
79
+ ...components,
80
+ };
81
+ }, [components]);
82
+
75
83
  const hasChildren = !!instance?.children?.length || instance?.insBom;
76
84
 
77
85
  return (
@@ -83,11 +91,33 @@ const Bar = (props: {
83
91
  />
84
92
  {!preview && extra.showLeftToolbar !== false ? (
85
93
  <>
86
- <Icon
87
- className={`${Styles.hover} ${Styles.link}`}
88
- type="icon-front-link1"
89
- />
94
+ <span
95
+ className={`${Styles.link} ${
96
+ open ? Styles.hiddenLinkCount : ''
97
+ }`}>
98
+ <DropDown
99
+ arrow={true}
100
+ showIcon={false}
101
+ disabled={!instance?.trackLinkCount}
102
+ stopCloseOnClickSelf={true}
103
+ buttonLabel={
104
+ // [TODO] 显示 trackLinkCount
105
+ <div>
106
+ <i>{instance?.trackLinkCount || ''}</i>
107
+ <StaticIcon
108
+ className={`${Styles.hover}`}
109
+ type="icon-front-link1"
110
+ />
111
+ </div>
112
+ }
113
+ onOpen={setOpen}>
114
+ {Components.TrackLinkList && instance ? (
115
+ <Components.TrackLinkList number={instance.number!} />
116
+ ) : null}
117
+ </DropDown>
118
+ </span>
90
119
  <DropDown
120
+ arrow={true}
91
121
  showIcon={false}
92
122
  buttonLabel={
93
123
  <Icon className={Styles.hover} type="icon-front-xinzeng1" />
package/src/bar/index.tsx CHANGED
@@ -25,7 +25,7 @@ import {$createInstanceNode, InstanceNode} from '../base';
25
25
  import {$getInstanceNodeByChild} from '../utils';
26
26
  import Bar from './BarComponent';
27
27
 
28
- export type SerializedPlaceholderDecoratorNode = Spread<
28
+ export type SerializedBarDecoratorNode = Spread<
29
29
  {
30
30
  __text: string;
31
31
  },
@@ -47,7 +47,7 @@ export class BarDecoratorNode extends DecoratorNode<JSX.Element> {
47
47
  }
48
48
 
49
49
  static importJSON(
50
- serializedNode: SerializedPlaceholderDecoratorNode,
50
+ serializedNode: SerializedBarDecoratorNode,
51
51
  ): BarDecoratorNode {
52
52
  return $createBarDecoratorNode().updateFromJSON(serializedNode);
53
53
  }
@@ -11,7 +11,33 @@
11
11
  flex-direction: column;
12
12
  align-items: center;
13
13
  .link {
14
- margin-bottom: 10px;
14
+ margin-bottom: 6px;
15
+ > button > span > div {
16
+ position: relative;
17
+ > i {
18
+ position: absolute;
19
+ font-size: 10px;
20
+ color: #fff;
21
+ right: -2px;
22
+ bottom: -2px;
23
+ width: 12px;
24
+ height: 12px;
25
+ border-radius: 50%;
26
+ display: flex;
27
+ font-style: normal;
28
+ align-items: center;
29
+ justify-content: center;
30
+ background-color: @themeColor;
31
+ &:empty {
32
+ display: none;
33
+ }
34
+ }
35
+ }
36
+ }
37
+ .hiddenLinkCount {
38
+ > button > span > div > i {
39
+ display: none;
40
+ }
15
41
  }
16
42
  > * {
17
43
  display: inline-flex;
package/src/const.ts CHANGED
@@ -64,6 +64,11 @@ export const numberNodeKey = new NumberNodeKey();
64
64
 
65
65
  export const fixedAddress = new Map<string, {value: Instance}>();
66
66
 
67
+ export const internalLinkNameUpdateMap = new Map<
68
+ string,
69
+ Map<string, () => void>
70
+ >();
71
+
67
72
  export const OPEN_CREATE_WINDOW: LexicalCommand<{
68
73
  isAddChildLevel: boolean;
69
74
  number: string;
@@ -77,3 +82,6 @@ export const ADD_NEW_INSTANCE_NODE: LexicalCommand<{
77
82
  }> = createCommand('ADD_NEW_INSTANCE_NODE');
78
83
 
79
84
  export const DELETE_INSTANCE_NODE = 'DELETE_INSTANCE_NODE';
85
+
86
+ export const INSERT_PARAMETERS: LexicalCommand<undefined> =
87
+ createCommand('INSERT_PARAMETERS');
package/src/index.ts CHANGED
@@ -63,6 +63,11 @@ export {
63
63
  type InlineImagePayload,
64
64
  } from './image/InlineImageNode';
65
65
  export {InstancePlugin} from './instancePlugin';
66
+ export {
67
+ $createInternalLinkNode,
68
+ $isInternalLinkNode,
69
+ InternalLinkNode,
70
+ } from './internalLink';
66
71
  export {$createKeywordNode, $isKeywordNode, KeywordNode} from './keyword';
67
72
  export {
68
73
  $createInstanceListNode,
@@ -0,0 +1,81 @@
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 {
10
+ $applyNodeReplacement,
11
+ EditorConfig,
12
+ LexicalEditor,
13
+ SerializedTextDecoratorNode,
14
+ Spread,
15
+ TextDecoratorNode,
16
+ } from 'lexical';
17
+ import React from 'react';
18
+
19
+ import InternalLinkComponent from './internalLinkComponent';
20
+
21
+ export type SerializedInternalNode = Spread<
22
+ {
23
+ __number: string;
24
+ },
25
+ SerializedTextDecoratorNode
26
+ >;
27
+ export class InternalLinkNode extends TextDecoratorNode<React.ReactNode> {
28
+ __number: string;
29
+ static getType(): string {
30
+ return 'InternalLink';
31
+ }
32
+
33
+ static clone(node: InternalLinkNode) {
34
+ return new InternalLinkNode(node.__number, node.__key);
35
+ }
36
+
37
+ static importJSON(serializedNode: SerializedInternalNode): InternalLinkNode {
38
+ return $createInternalLinkNode(serializedNode.__number).updateFromJSON(
39
+ serializedNode,
40
+ );
41
+ }
42
+
43
+ constructor(number: string, key?: string) {
44
+ super(key);
45
+ this.__number = number;
46
+ }
47
+
48
+ updateDOM(prevNode: this, dom: HTMLElement, config: EditorConfig): boolean {
49
+ return super.updateDOM(prevNode, dom, config);
50
+ }
51
+
52
+ collapseAtStart(): true {
53
+ return true;
54
+ }
55
+
56
+ isInline(): boolean {
57
+ return true;
58
+ }
59
+
60
+ decorate(editor: LexicalEditor, config: EditorConfig): JSX.Element {
61
+ return (
62
+ <InternalLinkComponent
63
+ editor={editor}
64
+ config={config}
65
+ self={this}
66
+ number={this.__number}
67
+ nodeKey={this.getKey()}
68
+ />
69
+ );
70
+ }
71
+ }
72
+
73
+ export function $createInternalLinkNode(number: string): InternalLinkNode {
74
+ return $applyNodeReplacement(new InternalLinkNode(number));
75
+ }
76
+
77
+ export function $isInternalLinkNode(
78
+ node: InternalLinkNode | null | undefined,
79
+ ): node is InternalLinkNode {
80
+ return node instanceof InternalLinkNode;
81
+ }
@@ -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
+ /* eslint-disable @lexical/rules-of-lexical */
9
+ /* eslint-disable jsx-a11y/no-static-element-interactions */
10
+
11
+ import type {InternalLinkNode} from '.';
12
+
13
+ import {EditorConfig, LexicalEditor} from 'lexical';
14
+ import {useCallback, useEffect, useState} from 'react';
15
+
16
+ import {internalLinkNameUpdateMap} from '../const';
17
+ import {$getInstanceNodeByNumber, $scrollTo, getLatestValue} from '../utils';
18
+ import Styles from './styles.module.less';
19
+
20
+ export default function InternalLinkComponent({
21
+ self,
22
+ editor,
23
+ config,
24
+ number,
25
+ nodeKey,
26
+ }: {
27
+ self: InternalLinkNode;
28
+ number: string;
29
+ nodeKey: string;
30
+ editor: LexicalEditor;
31
+ config: EditorConfig;
32
+ }) {
33
+ const [name, setName] = useState<string>();
34
+ const [serial, setSerial] = useState<string>();
35
+
36
+ const update = useCallback(function () {
37
+ editor.read(() => {
38
+ const node = $getInstanceNodeByNumber(number)!;
39
+ setName(getLatestValue(node.__instance.value, 'insDesc'));
40
+ setSerial(node.getSerialNumber());
41
+ });
42
+ }, []);
43
+
44
+ useEffect(() => {
45
+ update();
46
+ }, []);
47
+
48
+ useEffect(() => {
49
+ let timeout = 0;
50
+ let map: Map<string, () => void>;
51
+ const debounceUpdate = () => {
52
+ clearTimeout(timeout);
53
+ timeout = window.setTimeout(() => {
54
+ update();
55
+ }, 100);
56
+ };
57
+ if (internalLinkNameUpdateMap.has(number)) {
58
+ map = internalLinkNameUpdateMap.get(number)!.set(nodeKey, debounceUpdate);
59
+ } else {
60
+ map = new Map([[nodeKey, debounceUpdate]]);
61
+ internalLinkNameUpdateMap.set(number, map);
62
+ }
63
+ return () => {
64
+ map.delete(number);
65
+ };
66
+ }, []);
67
+
68
+ if (name) {
69
+ return (
70
+ <span
71
+ className={`${Styles.internalLink}`}
72
+ onClick={() =>
73
+ editor.update(() => {
74
+ $scrollTo(number);
75
+ })
76
+ }>
77
+ <span>{serial}</span>
78
+ <span>{name}</span>
79
+ </span>
80
+ );
81
+ } else {
82
+ return null;
83
+ }
84
+ }
@@ -0,0 +1,15 @@
1
+ @import './const.less';
2
+
3
+ .internalLink {
4
+ cursor: pointer;
5
+ color: @themeColor;
6
+ > span:nth-of-type(1):not(:empty) {
7
+ padding-right: 4px;
8
+ }
9
+ &:hover {
10
+ color: @themeColor;
11
+ > span:nth-of-type(2) {
12
+ text-decoration: underline;
13
+ }
14
+ }
15
+ }
@@ -29,7 +29,7 @@ import {
29
29
  } from 'lexical';
30
30
 
31
31
  import {$isInstanceNode} from '../base';
32
- import {Placeholder} from '../const';
32
+ import {internalLinkNameUpdateMap, Placeholder} from '../const';
33
33
  import {InstanceHeadingNode, isGoogleDocsTitle} from '../heading';
34
34
  import {
35
35
  $createPlaceholderDecoratorNode,
@@ -137,6 +137,10 @@ export class InstanceTitleNode extends InstanceHeadingNode {
137
137
  if (instanceNode && instanceNode.__instance.value) {
138
138
  const text = this.getTextContent().trim();
139
139
  setInstanceAttrValue(instanceNode.__instance.value, 'insDesc', text);
140
+ internalLinkNameUpdateMap
141
+ .get(instanceNode.__instance.value.number!)
142
+ ?.values()
143
+ .forEach((update) => update());
140
144
  }
141
145
  return super.updateDOM(prevNode, dom, config);
142
146
  }
package/src/types.d.ts CHANGED
@@ -25,6 +25,7 @@ export interface Instance
25
25
  extends InstanceBaseInfo,
26
26
  Partial<InstanceExtraAttributes> {
27
27
  children?: Instance[];
28
+ trackLinkCount?: number | null
28
29
  __contentText?: string;
29
30
  }
30
31
 
package/src/utils.ts CHANGED
@@ -29,7 +29,11 @@ import normalizeClassNames from 'shared/normalizeClassNames';
29
29
 
30
30
  import {$createBarDecoratorNode, $isBarDecoratorNode} from './bar';
31
31
  import {$isInstanceNode, InstanceNode} from './base';
32
- import {numberNodeKey, paragraphSymbol} from './const';
32
+ import {
33
+ internalLinkNameUpdateMap,
34
+ numberNodeKey,
35
+ paragraphSymbol,
36
+ } from './const';
33
37
  import {InstanceHeadingNode} from './heading';
34
38
  import {$isInstanceParagraphNode, InstanceParagraphNode} from './paragraph';
35
39
  import {$isInstanceTitleNode} from './paragraph/title';
@@ -218,6 +222,7 @@ export function $scrollTo(number: string) {
218
222
  export function clearCache() {
219
223
  paragraphSymbol.clear();
220
224
  numberNodeKey.clear();
225
+ internalLinkNameUpdateMap.clear();
221
226
  }
222
227
 
223
228
  export function $getInstanceNodeKeyByNumber(number: string) {
@@ -228,7 +233,19 @@ export function $getInstanceNodeKeyByNumber(number: string) {
228
233
  export function $getInstanceNodeByNumber(number: string) {
229
234
  const key = $getInstanceNodeKeyByNumber(number);
230
235
  if (key) {
231
- return $getNodeByKey(key);
236
+ return $getNodeByKey<InstanceNode>(key);
237
+ }
238
+ }
239
+
240
+ export function updateRelatedInternalLink<T extends ElementNode>(nodes: T[]) {
241
+ const insNodes = nodes.filter((node) => $isInstanceNode(node));
242
+ if (insNodes.length) {
243
+ insNodes.forEach((node) => {
244
+ internalLinkNameUpdateMap
245
+ .get(node.__instance.value.number!)
246
+ ?.values()
247
+ .forEach((update) => update());
248
+ });
232
249
  }
233
250
  }
234
251
 
@@ -244,6 +261,7 @@ export function nodeMoveUp<T extends ElementNode>(nodes?: T[] | null) {
244
261
  previous?.insertBefore(current);
245
262
  }
246
263
  }
264
+ updateRelatedInternalLink(nodes);
247
265
  }
248
266
  }
249
267
 
@@ -259,6 +277,7 @@ export function nodeMoveDown<T extends ElementNode>(nodes?: T[] | null) {
259
277
  next?.insertAfter(current);
260
278
  }
261
279
  }
280
+ updateRelatedInternalLink(nodes);
262
281
  }
263
282
  }
264
283
 
@@ -278,6 +297,7 @@ export async function $nodeUpgrade<T extends ElementNode>(nodes?: T[] | null) {
278
297
  if (bar) {
279
298
  bar.replace($createBarDecoratorNode());
280
299
  }
300
+ updateRelatedInternalLink(nodes);
281
301
  }
282
302
  }
283
303
 
@@ -295,6 +315,7 @@ export async function $nodeDowngrade<T extends ElementNode>(
295
315
  bar.replace($createBarDecoratorNode());
296
316
  }
297
317
  }
318
+ updateRelatedInternalLink(nodes);
298
319
  }
299
320
  }
300
321
 
@@ -1,11 +0,0 @@
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
- 'use strict';
10
-
11
- module.exports = require('./dist/OnchainLexicalInstance.js');