@fluidframework/react 2.90.0-378676 → 2.90.0

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.
Files changed (96) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +2 -0
  3. package/lib/index.d.ts +2 -0
  4. package/lib/index.d.ts.map +1 -1
  5. package/lib/index.js +2 -0
  6. package/lib/index.js.map +1 -1
  7. package/lib/propNode.js.map +1 -1
  8. package/lib/test/mochaHooks.js +13 -0
  9. package/lib/test/mochaHooks.js.map +1 -0
  10. package/lib/test/text/plainUtils.test.js +75 -0
  11. package/lib/test/text/plainUtils.test.js.map +1 -0
  12. package/lib/test/text/textEditor.test.js +704 -0
  13. package/lib/test/text/textEditor.test.js.map +1 -0
  14. package/lib/test/undoRedo.test.js +62 -0
  15. package/lib/test/undoRedo.test.js.map +1 -0
  16. package/lib/test/useObservation.spec.js +0 -1
  17. package/lib/test/useObservation.spec.js.map +1 -1
  18. package/lib/test/useTree.spec.js +0 -1
  19. package/lib/test/useTree.spec.js.map +1 -1
  20. package/lib/text/formatted/index.d.ts +6 -0
  21. package/lib/text/formatted/index.d.ts.map +1 -0
  22. package/lib/text/formatted/index.js +6 -0
  23. package/lib/text/formatted/index.js.map +1 -0
  24. package/lib/text/formatted/quillFormattedView.d.ts +54 -0
  25. package/lib/text/formatted/quillFormattedView.d.ts.map +1 -0
  26. package/lib/text/formatted/quillFormattedView.js +426 -0
  27. package/lib/text/formatted/quillFormattedView.js.map +1 -0
  28. package/lib/text/index.d.ts +7 -0
  29. package/lib/text/index.d.ts.map +1 -0
  30. package/lib/text/index.js +7 -0
  31. package/lib/text/index.js.map +1 -0
  32. package/lib/text/plain/index.d.ts +7 -0
  33. package/lib/text/plain/index.d.ts.map +1 -0
  34. package/lib/text/plain/index.js +7 -0
  35. package/lib/text/plain/index.js.map +1 -0
  36. package/lib/text/plain/plainTextView.d.ts +14 -0
  37. package/lib/text/plain/plainTextView.d.ts.map +1 -0
  38. package/lib/text/plain/plainTextView.js +75 -0
  39. package/lib/text/plain/plainTextView.js.map +1 -0
  40. package/lib/text/plain/plainUtils.d.ts +23 -0
  41. package/lib/text/plain/plainUtils.d.ts.map +1 -0
  42. package/lib/text/plain/plainUtils.js +51 -0
  43. package/lib/text/plain/plainUtils.js.map +1 -0
  44. package/lib/text/plain/quillView.d.ts +22 -0
  45. package/lib/text/plain/quillView.d.ts.map +1 -0
  46. package/lib/text/plain/quillView.js +112 -0
  47. package/lib/text/plain/quillView.js.map +1 -0
  48. package/lib/undoRedo.d.ts +51 -0
  49. package/lib/undoRedo.d.ts.map +1 -0
  50. package/lib/undoRedo.js +76 -0
  51. package/lib/undoRedo.js.map +1 -0
  52. package/package.json +26 -45
  53. package/react.test-files.tar +0 -0
  54. package/src/index.ts +10 -0
  55. package/src/propNode.ts +1 -1
  56. package/src/text/formatted/index.ts +11 -0
  57. package/src/text/formatted/quillFormattedView.tsx +509 -0
  58. package/src/text/index.ts +15 -0
  59. package/src/text/plain/index.ts +7 -0
  60. package/src/text/plain/plainTextView.tsx +110 -0
  61. package/src/text/plain/plainUtils.ts +68 -0
  62. package/src/text/plain/quillView.tsx +149 -0
  63. package/src/undoRedo.ts +117 -0
  64. package/tsconfig.json +6 -0
  65. package/api-extractor/api-extractor-lint-alpha.cjs.json +0 -5
  66. package/api-extractor/api-extractor-lint-beta.cjs.json +0 -5
  67. package/api-extractor/api-extractor-lint-public.cjs.json +0 -5
  68. package/dist/alpha.d.ts +0 -45
  69. package/dist/beta.d.ts +0 -15
  70. package/dist/index.d.ts +0 -16
  71. package/dist/index.d.ts.map +0 -1
  72. package/dist/index.js +0 -26
  73. package/dist/index.js.map +0 -1
  74. package/dist/package.json +0 -4
  75. package/dist/propNode.d.ts +0 -114
  76. package/dist/propNode.d.ts.map +0 -1
  77. package/dist/propNode.js +0 -43
  78. package/dist/propNode.js.map +0 -1
  79. package/dist/public.d.ts +0 -15
  80. package/dist/reactSharedTreeView.d.ts +0 -119
  81. package/dist/reactSharedTreeView.d.ts.map +0 -1
  82. package/dist/reactSharedTreeView.js +0 -206
  83. package/dist/reactSharedTreeView.js.map +0 -1
  84. package/dist/simpleIdentifier.d.ts +0 -19
  85. package/dist/simpleIdentifier.d.ts.map +0 -1
  86. package/dist/simpleIdentifier.js +0 -33
  87. package/dist/simpleIdentifier.js.map +0 -1
  88. package/dist/useObservation.d.ts +0 -83
  89. package/dist/useObservation.d.ts.map +0 -1
  90. package/dist/useObservation.js +0 -295
  91. package/dist/useObservation.js.map +0 -1
  92. package/dist/useTree.d.ts +0 -80
  93. package/dist/useTree.d.ts.map +0 -1
  94. package/dist/useTree.js +0 -137
  95. package/dist/useTree.js.map +0 -1
  96. package/tsconfig.cjs.json +0 -7
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @fluidframework/react
2
2
 
3
+ ## 2.90.0
4
+
5
+ ### Minor Changes
6
+
7
+ - @fluidframework/react no longer supports CommonJS ([#26575](https://github.com/microsoft/FluidFramework/pull/26575)) [995c1e44aad](https://github.com/microsoft/FluidFramework/commit/995c1e44aad7368e4e59ac4ff3a7237280506cde)
8
+
9
+ CommonJS support has been removed from `@fluidframework/react`.
10
+ This package currently only has alpha APIs, so this opportunity was taken to simplify and modernize it while we still can.
11
+
3
12
  ## 2.83.0
4
13
 
5
14
  Dependency updates only.
package/README.md CHANGED
@@ -6,6 +6,8 @@ This package currently has some experimental APIs focused on integrating SharedT
6
6
 
7
7
  ## Known Issues and Limitations
8
8
 
9
+ This package is currently ESM only and does not support CommonJS.
10
+
9
11
  These are a mix of issues that were encountered when authoring this package, as well as limitations of this package.
10
12
 
11
13
  Some of this logic would be useful for non-react applications: to avoid creating even more septate packages, that logic is not split into its own package.
package/lib/index.d.ts CHANGED
@@ -13,4 +13,6 @@ export type { NodeRecord, PropTreeNode, PropTreeNodeRecord, PropTreeValue, Unwra
13
13
  export { toPropTreeNode, toPropTreeRecord, unwrapPropTreeNode, unwrapPropTreeRecord, } from "./propNode.js";
14
14
  export { useTree, usePropTreeNode, usePropTreeRecord, useTreeObservations, withTreeObservations, withMemoizedTreeObservations, } from "./useTree.js";
15
15
  export { objectIdNumber } from "./simpleIdentifier.js";
16
+ export { FormattedMainView, PlainTextMainView, PlainQuillView, type FormattedMainViewProps, type PlainMainViewProps, type FormattedEditorHandle, } from "./text/index.js";
17
+ export { UndoRedoStacks, type UndoRedo } from "./undoRedo.js";
16
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AAEH,YAAY,EACX,oBAAoB,EACpB,aAAa,EACb,uBAAuB,GACvB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACN,cAAc,EACd,sBAAsB,EACtB,iBAAiB,GACjB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,YAAY,EACX,UAAU,EACV,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,wBAAwB,EACxB,sBAAsB,EACtB,SAAS,EACT,oBAAoB,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EACN,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EACN,OAAO,EACP,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,4BAA4B,GAC5B,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AAEH,YAAY,EACX,oBAAoB,EACpB,aAAa,EACb,uBAAuB,GACvB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACN,cAAc,EACd,sBAAsB,EACtB,iBAAiB,GACjB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,YAAY,EACX,UAAU,EACV,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,wBAAwB,EACxB,sBAAsB,EACtB,SAAS,EACT,oBAAoB,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EACN,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EACN,OAAO,EACP,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,4BAA4B,GAC5B,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,EACN,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,GAC1B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC"}
package/lib/index.js CHANGED
@@ -6,4 +6,6 @@ export { treeDataObject, treeDataObjectInternal, TreeViewComponent, } from "./re
6
6
  export { toPropTreeNode, toPropTreeRecord, unwrapPropTreeNode, unwrapPropTreeRecord, } from "./propNode.js";
7
7
  export { useTree, usePropTreeNode, usePropTreeRecord, useTreeObservations, withTreeObservations, withMemoizedTreeObservations, } from "./useTree.js";
8
8
  export { objectIdNumber } from "./simpleIdentifier.js";
9
+ export { FormattedMainView, PlainTextMainView, PlainQuillView, } from "./text/index.js";
10
+ export { UndoRedoStacks } from "./undoRedo.js";
9
11
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAYH,OAAO,EACN,cAAc,EACd,sBAAsB,EACtB,iBAAiB,GACjB,MAAM,0BAA0B,CAAC;AAalC,OAAO,EACN,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EACN,OAAO,EACP,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,4BAA4B,GAC5B,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n/**\n * Utilities for using SharedTree with React.\n * @packageDocumentation\n */\n\nexport type {\n\tIReactTreeDataObject,\n\tTreeViewProps,\n\tSchemaIncompatibleProps,\n} from \"./reactSharedTreeView.js\";\nexport {\n\ttreeDataObject,\n\ttreeDataObjectInternal,\n\tTreeViewComponent,\n} from \"./reactSharedTreeView.js\";\nexport type { ObservationOptions } from \"./useObservation.js\";\nexport type {\n\tNodeRecord,\n\tPropTreeNode,\n\tPropTreeNodeRecord,\n\tPropTreeValue,\n\tUnwrapPropTreeNode,\n\tUnwrapPropTreeNodeRecord,\n\tWrapPropTreeNodeRecord,\n\tWrapNodes,\n\tIsMappableObjectType,\n} from \"./propNode.js\";\nexport {\n\ttoPropTreeNode,\n\ttoPropTreeRecord,\n\tunwrapPropTreeNode,\n\tunwrapPropTreeRecord,\n} from \"./propNode.js\";\nexport {\n\tuseTree,\n\tusePropTreeNode,\n\tusePropTreeRecord,\n\tuseTreeObservations,\n\twithTreeObservations,\n\twithMemoizedTreeObservations,\n} from \"./useTree.js\";\nexport { objectIdNumber } from \"./simpleIdentifier.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAYH,OAAO,EACN,cAAc,EACd,sBAAsB,EACtB,iBAAiB,GACjB,MAAM,0BAA0B,CAAC;AAalC,OAAO,EACN,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EACN,OAAO,EACP,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,4BAA4B,GAC5B,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,EACN,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,GAId,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,cAAc,EAAiB,MAAM,eAAe,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n/**\n * Utilities for using SharedTree with React.\n * @packageDocumentation\n */\n\nexport type {\n\tIReactTreeDataObject,\n\tTreeViewProps,\n\tSchemaIncompatibleProps,\n} from \"./reactSharedTreeView.js\";\nexport {\n\ttreeDataObject,\n\ttreeDataObjectInternal,\n\tTreeViewComponent,\n} from \"./reactSharedTreeView.js\";\nexport type { ObservationOptions } from \"./useObservation.js\";\nexport type {\n\tNodeRecord,\n\tPropTreeNode,\n\tPropTreeNodeRecord,\n\tPropTreeValue,\n\tUnwrapPropTreeNode,\n\tUnwrapPropTreeNodeRecord,\n\tWrapPropTreeNodeRecord,\n\tWrapNodes,\n\tIsMappableObjectType,\n} from \"./propNode.js\";\nexport {\n\ttoPropTreeNode,\n\ttoPropTreeRecord,\n\tunwrapPropTreeNode,\n\tunwrapPropTreeRecord,\n} from \"./propNode.js\";\nexport {\n\tuseTree,\n\tusePropTreeNode,\n\tusePropTreeRecord,\n\tuseTreeObservations,\n\twithTreeObservations,\n\twithMemoizedTreeObservations,\n} from \"./useTree.js\";\nexport { objectIdNumber } from \"./simpleIdentifier.js\";\n\nexport {\n\tFormattedMainView,\n\tPlainTextMainView,\n\tPlainQuillView,\n\ttype FormattedMainViewProps,\n\ttype PlainMainViewProps,\n\ttype FormattedEditorHandle,\n} from \"./text/index.js\";\nexport { UndoRedoStacks, type UndoRedo } from \"./undoRedo.js\";\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"propNode.js","sourceRoot":"","sources":["../src/propNode.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA6EH;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CACjC,QAA8B;IAE9B,OAAO,QAAa,CAAC;AACtB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CACnC,KAAQ;IAER,OAAO,KAAoC,CAAC;AAC7C,CAAC;AAgDD;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAqC,IAAO;IACzE,OAAO,IAAmC,CAAC;AAC5C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAuB,IAAO;IAC7D,OAAO,IAA4C,CAAC;AACrD,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { ErasedType } from \"@fluidframework/core-interfaces\";\nimport type { TreeNode, TreeLeafValue } from \"@fluidframework/tree\";\n\n/**\n * A type erased TreeNode for use in react props.\n * @remarks\n * Read content from the node using {@link usePropTreeNode} or {@link usePropTreeRecord}.\n *\n * In events where tracking dependencies is not required, the node can be unwrapped using {@link unwrapPropTreeNode}.\n *\n * To convert a TreeNode to this type use {@link toPropTreeNode} or {@link toPropTreeRecord}.\n * @alpha\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-interface, @typescript-eslint/no-empty-object-type\nexport interface PropTreeNode<T extends TreeNode> extends ErasedType<[T, \"PropTreeNode\"]> {}\n\n/**\n * Type TreeNodes in T as {@link PropTreeNode}s.\n * @remarks\n * This only handles a few cases (TreeNode, structurally typed objects fields and arrays) and leaves other types as is.\n * Users which provide other types (e.g. maps) which contain TreeNodes will need to handle wrapping those themselves if the wrapping is desired.\n *\n * Users of this should not rely on a given use of TreeNode not being wrapped:\n * future changes to this API may add more cases which are wrapped, and this will be considered a non-breaking change.\n * @privateRemarks\n * Covering all cases is impossible, and trying to cover more with recursive mapped types can break some of the types by losing methods, private members, etc.\n * To mitigate this IsMappableObjectType is used for objects, and only mappable types, where the mapping actually impacted the type are modified.\n *\n * This is intended to cover the common cases, and users can handle other cases manually.\n * See the tests for this for more details.\n * @alpha\n */\nexport type WrapNodes<T> = T extends TreeNode\n\t? PropTreeNode<T>\n\t: T extends readonly (infer U)[]\n\t\t? readonly WrapNodes<U>[]\n\t\t: // `T extends (infer U)` distributes over unions, allowing WrapNodes<A|B> to be WrapNodes<A> | WrapNodes<B>.\n\t\t\tT extends infer U\n\t\t\t? IsMappableObjectType<\n\t\t\t\t\tU,\n\t\t\t\t\t{\n\t\t\t\t\t\t[P in keyof U]: WrapNodes<U[P]>;\n\t\t\t\t\t} extends U\n\t\t\t\t\t\t? // Returning U in this case (when assignable to the mapped type) avoids flatting named interfaces when they are unchanged.\n\t\t\t\t\t\t\tU\n\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t[P in keyof U]: WrapNodes<U[P]>;\n\t\t\t\t\t\t\t},\n\t\t\t\t\tT\n\t\t\t\t>\n\t\t\t: T;\n\n/**\n * Detect if a type is a simple structural object.\n * @remarks\n * This returns the true case if the type is entirely defined by its set of public properties.\n * More concretely, this indicates if creating a mapped type based on `T`\n * will be lossy due to details mapped types cannot access.\n *\n * This is shallow, and distributes over unions.\n *\n * This also returns the true case for primitive types since mapping over them leaves them unchanged if doing so in a generic context:\n * Mapping over a primitive does not leave them unchanged if done directly (not to a generic type parameter), but this can not detect that behavior.\n * This is fine as the use for this is to detect when making a mapped type from a generic type parameter would be lossy.\n * @system @alpha\n */\nexport type IsMappableObjectType<\n\tT,\n\tTrue = true,\n\tFalse = false,\n\tMapped = {\n\t\t[P in keyof T]: T[P];\n\t},\n> = [Mapped] extends [T] ? ([T] extends [Mapped] ? True : False) : False;\n\n/**\n * Casts a node from a {@link PropTreeNode} back to a TreeNode.\n * @remarks\n * This should only be done in scenarios where tracking observations is not required (such as event handlers),\n * or when taking care to handle invalidation manually.\n * @alpha\n */\nexport function unwrapPropTreeNode<T extends TreeNode | TreeLeafValue>(\n\tpropNode: PropTreeValue<T> | T,\n): T {\n\treturn propNode as T;\n}\n\n/**\n * {@link unwrapPropTreeNode} but for a {@link PropTreeNodeRecord}.\n * @alpha\n */\nexport function unwrapPropTreeRecord<T extends PropTreeNodeRecord>(\n\tprops: T,\n): UnwrapPropTreeNodeRecord<T> {\n\treturn props as UnwrapPropTreeNodeRecord<T>;\n}\n\n/**\n * {@inheritdoc unwrapPropTreeNode}\n * @alpha\n */\nexport type UnwrapPropTreeNode<T extends TreeLeafValue | PropTreeNode<TreeNode> | undefined> =\n\tT extends PropTreeNode<infer Node> ? Node : T;\n\n/**\n * Record that can contain TreeNodes.\n * @alpha\n */\nexport type NodeRecord = Record<string, TreeNode | TreeLeafValue>;\n\n/**\n * Type erase `TreeNode`s from a {@link NodeRecord} as a {@link PropTreeNode}.\n * @alpha\n */\nexport type WrapPropTreeNodeRecord<T extends NodeRecord> = {\n\treadonly [P in keyof T]: PropTreeValue<T[P]>;\n};\n\n/**\n * Type erase `TreeNode`s from a {@link NodeRecord} as a {@link PropTreeNode}.\n * @alpha\n */\nexport type UnwrapPropTreeNodeRecord<T extends PropTreeNodeRecord> = {\n\treadonly [P in keyof T]: UnwrapPropTreeNode<T[P]>;\n};\n\n/**\n * Type erase `TreeNode`s from a {@link NodeRecord} as a {@link PropTreeNode}.\n * @alpha\n */\nexport type PropTreeNodeRecord = Record<\n\tstring,\n\tTreeLeafValue | PropTreeNode<TreeNode> | undefined\n>;\n\n/**\n * Type erase a `TreeNode` from a `TreeNode | TreeLeafValue` as a {@link PropTreeNode}.\n * @alpha\n */\nexport type PropTreeValue<T extends TreeNode | TreeLeafValue | undefined> = T extends TreeNode\n\t? PropTreeNode<T>\n\t: T;\n\n/**\n * Type erase a TreeNode as a {@link PropTreeNode}.\n * @alpha\n */\nexport function toPropTreeNode<T extends TreeNode | TreeLeafValue>(node: T): PropTreeValue<T> {\n\treturn node as unknown as PropTreeValue<T>;\n}\n\n/**\n * Type erase a {@link NodeRecord} as a {@link PropTreeNodeRecord}.\n * @alpha\n */\nexport function toPropTreeRecord<T extends NodeRecord>(node: T): WrapPropTreeNodeRecord<T> {\n\treturn node as unknown as WrapPropTreeNodeRecord<T>;\n}\n"]}
1
+ {"version":3,"file":"propNode.js","sourceRoot":"","sources":["../src/propNode.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA6EH;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CACjC,QAA8B;IAE9B,OAAO,QAAa,CAAC;AACtB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CACnC,KAAQ;IAER,OAAO,KAAoC,CAAC;AAC7C,CAAC;AAgDD;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAqC,IAAO;IACzE,OAAO,IAAmC,CAAC;AAC5C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAuB,IAAO;IAC7D,OAAO,IAA4C,CAAC;AACrD,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { ErasedType } from \"@fluidframework/core-interfaces\";\nimport type { TreeNode, TreeLeafValue } from \"@fluidframework/tree\";\n\n/**\n * A type erased TreeNode for use in react props.\n * @remarks\n * Read content from the node using {@link usePropTreeNode} or {@link usePropTreeRecord}.\n *\n * In events where tracking dependencies is not required, the node can be unwrapped using {@link unwrapPropTreeNode}.\n *\n * To convert a TreeNode to this type use {@link toPropTreeNode} or {@link toPropTreeRecord}.\n * @alpha\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface PropTreeNode<T extends TreeNode> extends ErasedType<[T, \"PropTreeNode\"]> {}\n\n/**\n * Type TreeNodes in T as {@link PropTreeNode}s.\n * @remarks\n * This only handles a few cases (TreeNode, structurally typed objects fields and arrays) and leaves other types as is.\n * Users which provide other types (e.g. maps) which contain TreeNodes will need to handle wrapping those themselves if the wrapping is desired.\n *\n * Users of this should not rely on a given use of TreeNode not being wrapped:\n * future changes to this API may add more cases which are wrapped, and this will be considered a non-breaking change.\n * @privateRemarks\n * Covering all cases is impossible, and trying to cover more with recursive mapped types can break some of the types by losing methods, private members, etc.\n * To mitigate this IsMappableObjectType is used for objects, and only mappable types, where the mapping actually impacted the type are modified.\n *\n * This is intended to cover the common cases, and users can handle other cases manually.\n * See the tests for this for more details.\n * @alpha\n */\nexport type WrapNodes<T> = T extends TreeNode\n\t? PropTreeNode<T>\n\t: T extends readonly (infer U)[]\n\t\t? readonly WrapNodes<U>[]\n\t\t: // `T extends (infer U)` distributes over unions, allowing WrapNodes<A|B> to be WrapNodes<A> | WrapNodes<B>.\n\t\t\tT extends infer U\n\t\t\t? IsMappableObjectType<\n\t\t\t\t\tU,\n\t\t\t\t\t{\n\t\t\t\t\t\t[P in keyof U]: WrapNodes<U[P]>;\n\t\t\t\t\t} extends U\n\t\t\t\t\t\t? // Returning U in this case (when assignable to the mapped type) avoids flatting named interfaces when they are unchanged.\n\t\t\t\t\t\t\tU\n\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t[P in keyof U]: WrapNodes<U[P]>;\n\t\t\t\t\t\t\t},\n\t\t\t\t\tT\n\t\t\t\t>\n\t\t\t: T;\n\n/**\n * Detect if a type is a simple structural object.\n * @remarks\n * This returns the true case if the type is entirely defined by its set of public properties.\n * More concretely, this indicates if creating a mapped type based on `T`\n * will be lossy due to details mapped types cannot access.\n *\n * This is shallow, and distributes over unions.\n *\n * This also returns the true case for primitive types since mapping over them leaves them unchanged if doing so in a generic context:\n * Mapping over a primitive does not leave them unchanged if done directly (not to a generic type parameter), but this can not detect that behavior.\n * This is fine as the use for this is to detect when making a mapped type from a generic type parameter would be lossy.\n * @system @alpha\n */\nexport type IsMappableObjectType<\n\tT,\n\tTrue = true,\n\tFalse = false,\n\tMapped = {\n\t\t[P in keyof T]: T[P];\n\t},\n> = [Mapped] extends [T] ? ([T] extends [Mapped] ? True : False) : False;\n\n/**\n * Casts a node from a {@link PropTreeNode} back to a TreeNode.\n * @remarks\n * This should only be done in scenarios where tracking observations is not required (such as event handlers),\n * or when taking care to handle invalidation manually.\n * @alpha\n */\nexport function unwrapPropTreeNode<T extends TreeNode | TreeLeafValue>(\n\tpropNode: PropTreeValue<T> | T,\n): T {\n\treturn propNode as T;\n}\n\n/**\n * {@link unwrapPropTreeNode} but for a {@link PropTreeNodeRecord}.\n * @alpha\n */\nexport function unwrapPropTreeRecord<T extends PropTreeNodeRecord>(\n\tprops: T,\n): UnwrapPropTreeNodeRecord<T> {\n\treturn props as UnwrapPropTreeNodeRecord<T>;\n}\n\n/**\n * {@inheritdoc unwrapPropTreeNode}\n * @alpha\n */\nexport type UnwrapPropTreeNode<T extends TreeLeafValue | PropTreeNode<TreeNode> | undefined> =\n\tT extends PropTreeNode<infer Node> ? Node : T;\n\n/**\n * Record that can contain TreeNodes.\n * @alpha\n */\nexport type NodeRecord = Record<string, TreeNode | TreeLeafValue>;\n\n/**\n * Type erase `TreeNode`s from a {@link NodeRecord} as a {@link PropTreeNode}.\n * @alpha\n */\nexport type WrapPropTreeNodeRecord<T extends NodeRecord> = {\n\treadonly [P in keyof T]: PropTreeValue<T[P]>;\n};\n\n/**\n * Type erase `TreeNode`s from a {@link NodeRecord} as a {@link PropTreeNode}.\n * @alpha\n */\nexport type UnwrapPropTreeNodeRecord<T extends PropTreeNodeRecord> = {\n\treadonly [P in keyof T]: UnwrapPropTreeNode<T[P]>;\n};\n\n/**\n * Type erase `TreeNode`s from a {@link NodeRecord} as a {@link PropTreeNode}.\n * @alpha\n */\nexport type PropTreeNodeRecord = Record<\n\tstring,\n\tTreeLeafValue | PropTreeNode<TreeNode> | undefined\n>;\n\n/**\n * Type erase a `TreeNode` from a `TreeNode | TreeLeafValue` as a {@link PropTreeNode}.\n * @alpha\n */\nexport type PropTreeValue<T extends TreeNode | TreeLeafValue | undefined> = T extends TreeNode\n\t? PropTreeNode<T>\n\t: T;\n\n/**\n * Type erase a TreeNode as a {@link PropTreeNode}.\n * @alpha\n */\nexport function toPropTreeNode<T extends TreeNode | TreeLeafValue>(node: T): PropTreeValue<T> {\n\treturn node as unknown as PropTreeValue<T>;\n}\n\n/**\n * Type erase a {@link NodeRecord} as a {@link PropTreeNodeRecord}.\n * @alpha\n */\nexport function toPropTreeRecord<T extends NodeRecord>(node: T): WrapPropTreeNodeRecord<T> {\n\treturn node as unknown as WrapPropTreeNodeRecord<T>;\n}\n"]}
@@ -0,0 +1,13 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import globalJsdom from "global-jsdom";
6
+ // Set up JSDOM before any modules are loaded (Quill needs document at import time)
7
+ const cleanup = globalJsdom();
8
+ // Remove JSDOM after imports are done, but before we run any tests.
9
+ // Tests which require JSDOM can call globalJsdom() to setup their own clean dom.
10
+ before(() => {
11
+ cleanup();
12
+ });
13
+ //# sourceMappingURL=mochaHooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mochaHooks.js","sourceRoot":"","sources":["../../src/test/mochaHooks.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,WAAW,MAAM,cAAc,CAAC;AAEvC,mFAAmF;AACnF,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;AAE9B,oEAAoE;AACpE,iFAAiF;AACjF,MAAM,CAAC,GAAG,EAAE;IACX,OAAO,EAAE,CAAC;AACX,CAAC,CAAC,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport globalJsdom from \"global-jsdom\";\n\n// Set up JSDOM before any modules are loaded (Quill needs document at import time)\nconst cleanup = globalJsdom();\n\n// Remove JSDOM after imports are done, but before we run any tests.\n// Tests which require JSDOM can call globalJsdom() to setup their own clean dom.\nbefore(() => {\n\tcleanup();\n});\n"]}
@@ -0,0 +1,75 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { strict as assert } from "node:assert";
6
+ // Allow import of file being tested
7
+ // eslint-disable-next-line import-x/no-internal-modules
8
+ import { computeSync } from "../../text/plain/plainUtils.js";
9
+ describe("plainUtils", () => {
10
+ describe("computeSync", () => {
11
+ /**
12
+ * Calls computeSync, applies the returned ops to a copy of `existing`,
13
+ * asserts the result equals `final`, and returns the ops for further assertions.
14
+ */
15
+ function computeSyncAndValidate(existing, final) {
16
+ const ops = computeSync(existing, final);
17
+ const result = [...existing];
18
+ if (ops.remove) {
19
+ result.splice(ops.remove.start, ops.remove.end - ops.remove.start);
20
+ }
21
+ if (ops.insert) {
22
+ result.splice(ops.insert.location, 0, ...ops.insert.slice);
23
+ }
24
+ assert.deepEqual(result, [...final]);
25
+ return ops;
26
+ }
27
+ it("works for two empty arrays", () => {
28
+ computeSyncAndValidate([], []);
29
+ });
30
+ it("returns no ops for identical arrays", () => {
31
+ const ops = computeSyncAndValidate(["a", "b", "c"], ["a", "b", "c"]);
32
+ assert.equal(ops.remove, undefined);
33
+ assert.equal(ops.insert, undefined);
34
+ });
35
+ it("inserts all elements when existing is empty", () => {
36
+ computeSyncAndValidate([], ["a", "b", "c"]);
37
+ });
38
+ it("removes all elements when final is empty", () => {
39
+ computeSyncAndValidate(["a", "b", "c"], []);
40
+ });
41
+ it("replaces all elements when arrays are completely different", () => {
42
+ computeSyncAndValidate(["a", "b"], ["c", "d"]);
43
+ });
44
+ it("appends element to end", () => {
45
+ const ops = computeSyncAndValidate(["a", "b"], ["a", "b", "c"]);
46
+ assert.equal(ops.remove, undefined);
47
+ });
48
+ it("removes element from end", () => {
49
+ const ops = computeSyncAndValidate(["a", "b", "c"], ["a", "b"]);
50
+ assert.equal(ops.insert, undefined);
51
+ });
52
+ it("prepends element at start", () => {
53
+ const ops = computeSyncAndValidate(["b", "c"], ["a", "b", "c"]);
54
+ assert.equal(ops.remove, undefined);
55
+ });
56
+ it("removes element from start", () => {
57
+ const ops = computeSyncAndValidate(["a", "b", "c"], ["b", "c"]);
58
+ assert.equal(ops.insert, undefined);
59
+ });
60
+ it("replaces middle section", () => {
61
+ const ops = computeSyncAndValidate(["a", "b", "c", "d"], ["a", "x", "y", "d"]);
62
+ assert.deepEqual(ops.remove, { start: 1, end: 3 });
63
+ assert.deepEqual(ops.insert, { location: 1, slice: ["x", "y"] });
64
+ });
65
+ it("inserts into the middle of an existing array", () => {
66
+ const ops = computeSyncAndValidate(["a", "d"], ["a", "b", "c", "d"]);
67
+ assert.equal(ops.remove, undefined);
68
+ });
69
+ it("removes from the middle of an existing array", () => {
70
+ const ops = computeSyncAndValidate(["a", "b", "c", "d"], ["a", "d"]);
71
+ assert.equal(ops.insert, undefined);
72
+ });
73
+ });
74
+ });
75
+ //# sourceMappingURL=plainUtils.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plainUtils.test.js","sourceRoot":"","sources":["../../../src/test/text/plainUtils.test.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { strict as assert } from \"node:assert\";\n\n// Allow import of file being tested\n// eslint-disable-next-line import-x/no-internal-modules\nimport { computeSync } from \"../../text/plain/plainUtils.js\";\n\ndescribe(\"plainUtils\", () => {\n\tdescribe(\"computeSync\", () => {\n\t\t/**\n\t\t * Calls computeSync, applies the returned ops to a copy of `existing`,\n\t\t * asserts the result equals `final`, and returns the ops for further assertions.\n\t\t */\n\t\tfunction computeSyncAndValidate<T>(\n\t\t\texisting: readonly T[],\n\t\t\tfinal: readonly T[],\n\t\t): ReturnType<typeof computeSync<T>> {\n\t\t\tconst ops = computeSync(existing, final);\n\t\t\tconst result = [...existing];\n\t\t\tif (ops.remove) {\n\t\t\t\tresult.splice(ops.remove.start, ops.remove.end - ops.remove.start);\n\t\t\t}\n\t\t\tif (ops.insert) {\n\t\t\t\tresult.splice(ops.insert.location, 0, ...ops.insert.slice);\n\t\t\t}\n\t\t\tassert.deepEqual(result, [...final]);\n\t\t\treturn ops;\n\t\t}\n\n\t\tit(\"works for two empty arrays\", () => {\n\t\t\tcomputeSyncAndValidate([], []);\n\t\t});\n\n\t\tit(\"returns no ops for identical arrays\", () => {\n\t\t\tconst ops = computeSyncAndValidate([\"a\", \"b\", \"c\"], [\"a\", \"b\", \"c\"]);\n\t\t\tassert.equal(ops.remove, undefined);\n\t\t\tassert.equal(ops.insert, undefined);\n\t\t});\n\n\t\tit(\"inserts all elements when existing is empty\", () => {\n\t\t\tcomputeSyncAndValidate([], [\"a\", \"b\", \"c\"]);\n\t\t});\n\n\t\tit(\"removes all elements when final is empty\", () => {\n\t\t\tcomputeSyncAndValidate([\"a\", \"b\", \"c\"], []);\n\t\t});\n\n\t\tit(\"replaces all elements when arrays are completely different\", () => {\n\t\t\tcomputeSyncAndValidate([\"a\", \"b\"], [\"c\", \"d\"]);\n\t\t});\n\n\t\tit(\"appends element to end\", () => {\n\t\t\tconst ops = computeSyncAndValidate([\"a\", \"b\"], [\"a\", \"b\", \"c\"]);\n\t\t\tassert.equal(ops.remove, undefined);\n\t\t});\n\n\t\tit(\"removes element from end\", () => {\n\t\t\tconst ops = computeSyncAndValidate([\"a\", \"b\", \"c\"], [\"a\", \"b\"]);\n\t\t\tassert.equal(ops.insert, undefined);\n\t\t});\n\n\t\tit(\"prepends element at start\", () => {\n\t\t\tconst ops = computeSyncAndValidate([\"b\", \"c\"], [\"a\", \"b\", \"c\"]);\n\t\t\tassert.equal(ops.remove, undefined);\n\t\t});\n\n\t\tit(\"removes element from start\", () => {\n\t\t\tconst ops = computeSyncAndValidate([\"a\", \"b\", \"c\"], [\"b\", \"c\"]);\n\t\t\tassert.equal(ops.insert, undefined);\n\t\t});\n\n\t\tit(\"replaces middle section\", () => {\n\t\t\tconst ops = computeSyncAndValidate([\"a\", \"b\", \"c\", \"d\"], [\"a\", \"x\", \"y\", \"d\"]);\n\t\t\tassert.deepEqual(ops.remove, { start: 1, end: 3 });\n\t\t\tassert.deepEqual(ops.insert, { location: 1, slice: [\"x\", \"y\"] });\n\t\t});\n\n\t\tit(\"inserts into the middle of an existing array\", () => {\n\t\t\tconst ops = computeSyncAndValidate([\"a\", \"d\"], [\"a\", \"b\", \"c\", \"d\"]);\n\t\t\tassert.equal(ops.remove, undefined);\n\t\t});\n\n\t\tit(\"removes from the middle of an existing array\", () => {\n\t\t\tconst ops = computeSyncAndValidate([\"a\", \"b\", \"c\", \"d\"], [\"a\", \"d\"]);\n\t\t\tassert.equal(ops.insert, undefined);\n\t\t});\n\t});\n});\n"]}