({
}
}
})
+
+export const handleListItemBackspace = (editor: Editor, name: string, parentListTypes: string[]): boolean => {
+ // this is required to still handle the undo handling
+ if (editor.commands.undoInputRule()) {
+ return true
+ }
+
+ // if the selection is not collapsed
+ // we can rely on the default backspace behavior
+ if (editor.state.selection.from !== editor.state.selection.to) {
+ return false
+ }
+
+ // if the current item is NOT inside a list item &
+ // the previous item is a list (orderedList or bulletList)
+ // move the cursor into the list and delete the current item
+ if (!isNodeActive(editor.state, name) && listHelpers.hasListBefore(editor.state, name, parentListTypes)) {
+ const { $anchor } = editor.state.selection
+
+ const $listPos = editor.state.doc.resolve($anchor.before() - 1)
+
+ const listDescendants: Array<{ node: Node, pos: number }> = []
+
+ $listPos.node().descendants((node, pos) => {
+ if (node.type.isInGroup('listItems')) {
+ listDescendants.push({ node, pos })
+ }
+ })
+
+ const lastItem = listDescendants.at(-1)
+
+ if (lastItem === undefined) {
+ return false
+ }
+
+ const $lastItemPos = editor.state.doc.resolve($listPos.start() + lastItem.pos + 1)
+
+ return editor
+ .chain()
+ .cut({ from: $anchor.start() - 1, to: $anchor.end() + 1 }, $lastItemPos.end())
+ .joinForward()
+ .run()
+ }
+
+ // if the cursor is not inside the current node type
+ // do nothing and proceed
+ if (!isNodeActive(editor.state, name)) {
+ return false
+ }
+
+ const $from = editor.state.selection.$from
+ const parentOffset = $from.depth > 0 ? $from.index($from.depth - 1) : 0
+
+ // if the cursor is not at the start of a node
+ // do nothing and proceed
+ if (!isAtStartOfNode(editor.state) || parentOffset > 0) {
+ return false
+ }
+
+ const listItemPos = findListItemPos(editor.state)
+ if (listItemPos === null) {
+ return false
+ }
+
+ const $prev = editor.state.doc.resolve(listItemPos.$pos.pos - 2)
+ const prevNode = $prev.node(listItemPos.depth)
+
+ const previousListItemHasSubList = listItemHasSubList(prevNode)
+
+ if (hasListItemBefore(editor.state)) {
+ // if the previous item is a list item and doesn't have a sublist, join the list items
+ if (!previousListItemHasSubList) {
+ return editor.commands.joinItemBackward()
+ } else {
+ return editor.chain().sinkListItem(name).joinItemBackward().run()
+ }
+ }
+
+ // otherwise in the end, a backspace should
+ // always just lift the list item if
+ // joining / merging is not possible
+ return editor.chain().liftListItem(name).run()
+}
+
+const findListItemPos = (state: EditorState): { $pos: ResolvedPos, depth: number } | null => {
+ const { $from } = state.selection
+
+ let currentNode = null
+ let currentDepth = $from.depth
+ let currentPos = $from.pos
+ let targetDepth: number | null = null
+
+ while (currentDepth > 0 && targetDepth === null) {
+ currentNode = $from.node(currentDepth)
+
+ if (currentNode.type.isInGroup('listItems')) {
+ targetDepth = currentDepth
+ } else {
+ currentDepth -= 1
+ currentPos -= 1
+ }
+ }
+
+ if (targetDepth === null) {
+ return null
+ }
+
+ return { $pos: state.doc.resolve(currentPos), depth: targetDepth }
+}
+
+const listItemHasSubList = (node?: Node): boolean => {
+ if (node === undefined) {
+ return false
+ }
+
+ let hasSubList = false
+
+ node.descendants((child) => {
+ if (child.type.isInGroup('listItems')) {
+ hasSubList = true
+ }
+ })
+
+ return hasSubList
+}
+
+const hasListItemBefore = (state: EditorState): boolean => {
+ const { $anchor } = state.selection
+
+ const $targetPos = state.doc.resolve($anchor.pos - 2)
+
+ if ($targetPos.index() === 0) {
+ return false
+ }
+
+ if (!($targetPos.nodeBefore?.type.isInGroup('listItems') ?? false)) {
+ return false
+ }
+
+ return true
+}
diff --git a/plugins/text-editor-resources/src/components/extension/popups/ColorPicker.svelte b/plugins/text-editor-resources/src/components/extension/popups/ColorPicker.svelte
index b199769bd2..d536c97adc 100644
--- a/plugins/text-editor-resources/src/components/extension/popups/ColorPicker.svelte
+++ b/plugins/text-editor-resources/src/components/extension/popups/ColorPicker.svelte
@@ -17,6 +17,7 @@
import { Card } from '@hcengineering/presentation'
export let palette: Array<{ color: string, preview?: string }> = [{ color: 'transparent' }]
+ export let letters: boolean = false
const dispatch = createEventDispatcher()
@@ -32,11 +33,15 @@
{
handleSubmit(k)
}}
- />
+ >
+ {#if letters}A{/if}
+
{/each}
@@ -61,6 +66,20 @@
height: 1.5rem;
border-radius: 0.25rem;
cursor: pointer;
+ }
+
+ .solid {
+ background-color: var(--color);
box-shadow: var(--text-editor-color-picker-outline) 0px 0px 0px 1px inset;
}
+
+ .letters {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: transparent;
+ border: 1px solid var(--color);
+ color: var(--color);
+ font-weight: bold;
+ }
diff --git a/server-plugins/controlled-documents-resources/src/index.ts b/server-plugins/controlled-documents-resources/src/index.ts
index a078023fb2..6d9db5e80b 100644
--- a/server-plugins/controlled-documents-resources/src/index.ts
+++ b/server-plugins/controlled-documents-resources/src/index.ts
@@ -9,7 +9,7 @@ import documents, {
DocumentApprovalRequest,
DocumentState,
DocumentTemplate,
- getEffectiveDocUpdate,
+ getEffectiveDocUpdates,
type DocumentRequest,
type DocumentTraining
} from '@hcengineering/controlled-documents'
@@ -54,8 +54,9 @@ async function getDocs (
return allDocs.filter(predicate)
}
-function makeDocEffective (doc: ControlledDocument, txFactory: TxFactory): Tx {
- return txFactory.createTxUpdateDoc(doc._class, doc.space, doc._id, getEffectiveDocUpdate())
+function makeDocEffective (doc: ControlledDocument, txFactory: TxFactory): Tx[] {
+ const updates = getEffectiveDocUpdates()
+ return updates.map((u) => txFactory.createTxUpdateDoc(doc._class, doc.space, doc._id, u))
}
function archiveDocs (docs: ControlledDocument[], txFactory: TxFactory): Tx[] {
@@ -339,7 +340,7 @@ export async function OnDocPlannedEffectiveDateChanged (
if (tx.operations.plannedEffectiveDate === 0 && doc.controlledState === ControlledDocumentState.Approved) {
// Create with not derived tx factory in order for notifications to work
const factory = new TxFactory(control.txFactory.account)
- await control.apply(control.ctx, [makeDocEffective(doc, factory)])
+ await control.apply(control.ctx, makeDocEffective(doc, factory))
}
}
@@ -364,7 +365,7 @@ export async function OnDocApprovalRequestApproved (
// Create with not derived tx factory in order for notifications to work
const factory = new TxFactory(control.txFactory.account)
- await control.apply(control.ctx, [makeDocEffective(doc, factory)])
+ await control.apply(control.ctx, makeDocEffective(doc, factory))
// make doc effective immediately
}
return result