mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-13 11:50:56 +00:00
Added focus toolbar for tables in text editor and adjusted table styling (#7509)
This commit is contained in:
parent
45745f54c5
commit
8a4c23fb23
@ -299,14 +299,23 @@ export function createModel (builder: Builder): void {
|
||||
|
||||
// Table category
|
||||
builder.createDoc(textEditor.class.TextEditorAction, core.space.Model, {
|
||||
action: textEditor.function.OpenTableOptions,
|
||||
icon: textEditor.icon.TableProps,
|
||||
visibilityTester: textEditor.function.IsEditableTableActive,
|
||||
label: textEditor.string.TableOptions,
|
||||
action: textEditor.function.SelectTable,
|
||||
icon: textEditor.icon.SelectTable,
|
||||
visibilityTester: textEditor.function.IsTableToolbarContext,
|
||||
label: textEditor.string.SelectTable,
|
||||
category: 70,
|
||||
index: 5
|
||||
})
|
||||
|
||||
builder.createDoc(textEditor.class.TextEditorAction, core.space.Model, {
|
||||
action: textEditor.function.OpenTableOptions,
|
||||
icon: textEditor.icon.TableProps,
|
||||
visibilityTester: textEditor.function.IsTableToolbarContext,
|
||||
label: textEditor.string.TableOptions,
|
||||
category: 70,
|
||||
index: 10
|
||||
})
|
||||
|
||||
// Image align category
|
||||
createImageAlignmentAction(builder, 'left')
|
||||
createImageAlignmentAction(builder, 'center')
|
||||
|
@ -25,6 +25,7 @@ export default mergeIds(textEditorId, textEditor, {
|
||||
function: {
|
||||
FormatLink: '' as Resource<TextActionFunction>,
|
||||
OpenTableOptions: '' as Resource<TextActionFunction>,
|
||||
SelectTable: '' as Resource<TextActionFunction>,
|
||||
OpenImage: '' as Resource<TextActionFunction>,
|
||||
ExpandImage: '' as Resource<TextActionFunction>,
|
||||
MoreImageActions: '' as Resource<TextActionFunction>,
|
||||
@ -32,6 +33,7 @@ export default mergeIds(textEditorId, textEditor, {
|
||||
DownloadImage: '' as Resource<TextActionFunction>,
|
||||
|
||||
IsEditableTableActive: '' as Resource<TextActionVisibleFunction>,
|
||||
IsTableToolbarContext: '' as Resource<TextActionVisibleFunction>,
|
||||
IsEditableNote: '' as Resource<TextActionVisibleFunction>,
|
||||
IsEditable: '' as Resource<TextActionVisibleFunction>,
|
||||
IsHeadingVisible: '' as Resource<TextActionVisibleFunction>,
|
||||
|
@ -309,7 +309,6 @@
|
||||
|
||||
--text-editor-toc-default-color: rgba(255, 255, 255, 0.1);
|
||||
--text-editor-toc-hovered-color: rgba(255, 255, 255, 0.4);
|
||||
--text-editor-drag-marker-bg-color: #444248;
|
||||
--text-editor-table-header-color: rgba(255, 255, 255, 0.06);
|
||||
|
||||
--theme-clockface-back: radial-gradient(farthest-corner at 50% 0%, #bbb, #fff 100%);
|
||||
@ -568,7 +567,6 @@
|
||||
|
||||
--text-editor-toc-default-color: rgba(0, 0, 0, 0.1);
|
||||
--text-editor-toc-hovered-color: rgba(0, 0, 0, 0.4);
|
||||
--text-editor-drag-marker-bg-color: #444248;
|
||||
--text-editor-table-header-color: rgba(0, 0, 0, 0.06);
|
||||
|
||||
--theme-clockface-back: radial-gradient(farthest-corner at 50% 0%, #606060, #000 100%);
|
||||
|
@ -15,14 +15,17 @@
|
||||
|
||||
/* Table */
|
||||
table.proseTable {
|
||||
--table-selection-border-width: 2px;
|
||||
--table-selection-border-indent: -2px;
|
||||
--table-selection-border-width: 1px;
|
||||
--table-selection-border-indent: -1px;
|
||||
--table-selection-border-radius: 2px;
|
||||
--table-handle-size: 1.25rem;
|
||||
--table-handle-indent: calc(-1.25rem - 1px);
|
||||
--table-handle-size: 0.875rem;
|
||||
--table-handle-indent: calc(var(--table-handle-size) * -1 - 1px);
|
||||
--table-handle-col-indent: calc(var(--table-handle-size) * -0.5);
|
||||
--table-handle-row-indent: calc(var(--table-handle-size) * -1 - 0.75rem);
|
||||
--table-insert-marker-indent: calc(-1.25rem - 1px);
|
||||
|
||||
--table-selection-z-index: 100;
|
||||
--table-drag-and-drop-z-index: 110;
|
||||
--table-drag-and-drop-z-index: 130;
|
||||
--table-handlers-z-index: 120;
|
||||
|
||||
border-collapse: collapse;
|
||||
@ -63,7 +66,6 @@ table.proseTable {
|
||||
&::before {
|
||||
content: '';
|
||||
border: 0 solid var(--primary-button-focused);
|
||||
border-radius: var(--table-selection-border-radius);
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
z-index: var(--table-selection-z-index);
|
||||
@ -100,10 +102,12 @@ table.proseTable {
|
||||
align-items: center;
|
||||
|
||||
button {
|
||||
background-color: transparent;
|
||||
background-color: var(--button-border-color);
|
||||
border-radius: var(--table-selection-border-radius);
|
||||
opacity: 0;
|
||||
transition: background-color 0.3s ease-in-out;
|
||||
transition-property: opacity, background-color;
|
||||
transition-timing-function: ease-in-out;
|
||||
transition-duration: 0.1s;
|
||||
|
||||
svg {
|
||||
color: var(--theme-button-contrast-hovered);
|
||||
@ -113,8 +117,6 @@ table.proseTable {
|
||||
&__selected {
|
||||
&::before {
|
||||
content: '';
|
||||
background-color: var(--primary-button-focused);
|
||||
border: var(--table-selection-border-width) solid var(--primary-button-focused);
|
||||
border-radius: var(--table-selection-border-radius);
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
@ -125,9 +127,10 @@ table.proseTable {
|
||||
right: var(--table-selection-border-indent);
|
||||
}
|
||||
|
||||
button {
|
||||
&:hover button {
|
||||
opacity: 1;
|
||||
z-index: var(--table-handlers-z-index);
|
||||
background-color: var(--primary-button-default);
|
||||
|
||||
svg {
|
||||
color: white;
|
||||
@ -147,20 +150,20 @@ table.proseTable {
|
||||
|
||||
.table-col-handle {
|
||||
position: absolute;
|
||||
width: calc(100% + 1px);
|
||||
height: var(--table-handle-size);
|
||||
top: var(--table-handle-indent);
|
||||
left: 0;
|
||||
top: var(--table-handle-col-indent);
|
||||
left: -1px;
|
||||
right: -1px;
|
||||
|
||||
button {
|
||||
height: 70%;
|
||||
height: 100%;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&:not(.table-col-handle__selected) {
|
||||
background-color: var(--theme-button-hovered);
|
||||
}
|
||||
border-radius: var(--table-selection-border-radius);
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
|
||||
button {
|
||||
opacity: 1;
|
||||
@ -168,8 +171,9 @@ table.proseTable {
|
||||
}
|
||||
|
||||
&__selected {
|
||||
left: 0;
|
||||
&::before {
|
||||
right: -1px;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
border-bottom-width: 0;
|
||||
@ -182,12 +186,15 @@ table.proseTable {
|
||||
.table-row-handle {
|
||||
position: absolute;
|
||||
width: var(--table-handle-size);
|
||||
height: calc(100% + 1px);
|
||||
top: 0;
|
||||
left: var(--table-handle-indent);
|
||||
top: -1px;
|
||||
bottom: -1px;
|
||||
left: var(--table-handle-row-indent);
|
||||
border-radius: var(--table-selection-border-radius);
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
|
||||
button {
|
||||
width: 70%;
|
||||
width: 100%;
|
||||
padding: 4px 0;
|
||||
|
||||
svg {
|
||||
@ -197,7 +204,6 @@ table.proseTable {
|
||||
|
||||
&:hover {
|
||||
&:not(.table-row-handle__selected) {
|
||||
background-color: var(--theme-button-hovered);
|
||||
}
|
||||
|
||||
button {
|
||||
@ -206,8 +212,9 @@ table.proseTable {
|
||||
}
|
||||
|
||||
&__selected {
|
||||
top: 0;
|
||||
&::before {
|
||||
bottom: -1px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
border-right-width: 0;
|
||||
@ -230,12 +237,12 @@ table.proseTable {
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
top: var(--table-handle-indent);
|
||||
top: var(--table-insert-marker-indent);
|
||||
right: -0.625rem;
|
||||
width: 1.25rem;
|
||||
|
||||
.table-insert-marker {
|
||||
width: 0.125rem;
|
||||
width: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -244,12 +251,12 @@ table.proseTable {
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
left: var(--table-handle-indent);
|
||||
left: var(--table-insert-marker-indent);
|
||||
bottom: -0.625rem;
|
||||
height: 1.25rem;
|
||||
|
||||
.table-insert-marker {
|
||||
height: 0.125rem;
|
||||
height: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,11 +265,14 @@ table.proseTable {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
|
||||
opacity: 0;
|
||||
|
||||
svg {
|
||||
color: white;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
border-radius: 50%;
|
||||
background-color: var(--primary-button-focused);
|
||||
}
|
||||
@ -301,24 +311,41 @@ table.proseTable {
|
||||
.table-row-drag-marker {
|
||||
position: absolute;
|
||||
z-index: var(--table-drag-and-drop-z-index);
|
||||
opacity: 0.5;
|
||||
background-color: var(--text-editor-drag-marker-bg-color);
|
||||
border: var(--table-selection-border-width) solid var(--text-editor-drag-marker-bg-color);
|
||||
border-radius: var(--table-selection-border-radius);
|
||||
background-color: transparent;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
button {
|
||||
margin: auto;
|
||||
background-color: var(--button-border-color);
|
||||
border-radius: var(--table-selection-border-radius);
|
||||
}
|
||||
|
||||
svg {
|
||||
color: white;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.table-col-drag-marker {
|
||||
height: var(--table-handle-size);
|
||||
top: calc(var(--table-handle-indent) + 1px);
|
||||
top: var(--table-handle-col-indent);
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background-color: var(--primary-button-focused);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
button {
|
||||
height: 100%;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 100%;
|
||||
@ -327,7 +354,12 @@ table.proseTable {
|
||||
|
||||
.table-row-drag-marker {
|
||||
width: var(--table-handle-size);
|
||||
left: calc(var(--table-handle-indent) + 1px / 2);
|
||||
left: var(--table-handle-row-indent);
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
svg {
|
||||
height: 100%;
|
||||
|
@ -201,4 +201,10 @@
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M27.4,25.4L27.4,25.4c0.2,0,0.4,0,0.6-0.1c0.2-0.1,0.3-0.2,0.5-0.3c0.1-0.1,0.2-0.3,0.3-0.5C29,24.2,29,24,29,23.9v-0.1c0-0.1,0-0.2-0.1-0.4l-1-3.5c0,0,0-0.1,0.1-0.1c0,0,0-0.1,0.1-0.1l0,0c1.2-1.8,1.9-4,1.9-6.2c0-3-1.2-5.8-3.3-7.9C24.4,3.3,21.4,2,18.1,2c-2.7,0-5.4,0.9-7.5,2.6c-2.1,1.7-3.5,4-4.1,6.5c-0.2,0.8-0.3,1.6-0.3,2.4c0,1.5,0.3,3,0.9,4.4c0.6,1.4,1.4,2.7,2.5,3.7c2.2,2.2,5.2,3.4,8.3,3.4c1.2,0,2.8-0.4,3.2-0.5c0.8-0.2,1.6-0.5,1.7-0.5c0.1,0,0.2,0,0.2,0c0.1,0,0.2,0,0.2,0l0,0l3.5,1.3C27.1,25.3,27.2,25.3,27.4,25.4L27.4,25.4z M26.7,23l-2.7-1h0c-0.6-0.2-1.3-0.3-1.9,0c0,0,0,0,0,0c-0.2,0.1-0.8,0.3-1.5,0.5C19.9,22.8,18.7,23,18,23c-5.3,0-9.7-4.3-9.7-9.6c0-0.6,0.1-1.3,0.2-1.9c0.9-4.4,5-7.5,9.7-7.5c2.7,0,5.3,1,7.1,2.9C27,8.7,28,11,28,13.4c0,1.8-0.5,3.6-1.5,5.1c-0.1,0.1-0.1,0.2-0.2,0.3l0,0c-0.3,0.6-0.4,1.1-0.3,1.5L26.7,23z" />
|
||||
<path d="M3.6,29.7C3.8,29.9,4.2,30,4.5,30h0c0.2,0,0.4,0,0.6-0.1l3.5-1.4l0,0c1.4,0.5,2.8,0.8,4.2,0.8h0c1.9,0,3.8-0.5,5.5-1.5c0.1-0.1,0.2-0.2,0.3-0.3c0.1-0.1,0.1-0.2,0.2-0.4s0-0.3,0-0.4c0-0.1-0.1-0.3-0.1-0.4c-0.1-0.1-0.2-0.2-0.3-0.3c-0.1-0.1-0.2-0.1-0.4-0.2c-0.1,0-0.3,0-0.4,0c-0.1,0-0.3,0.1-0.4,0.1c-1.4,0.8-2.9,1.2-4.5,1.2h0c-1.1,0-2.3-0.2-3.4-0.7c-0.5-0.2-1-0.2-1.5,0l-2.7,1c0.3-1.3,0.5-2.8,0.6-2.9c0.1-0.5-0.1-0.9-0.3-1.3l0,0c-0.8-1.2-1.3-2.7-1.4-4.2c-0.1-1.5,0.3-3,1-4.3c0.1-0.2,0.2-0.5,0.1-0.8C5,14,4.9,13.8,4.6,13.6c-0.2-0.1-0.5-0.2-0.8-0.1c-0.3,0.1-0.5,0.2-0.6,0.5c-0.9,1.6-1.3,3.5-1.3,5.4c0.1,1.9,0.7,3.7,1.7,5.2v0c-0.1,0.5-0.3,1.7-0.5,2.7C3.1,27.7,3.1,28,3,28.2C3,28.5,3,28.8,3.1,29C3.2,29.3,3.3,29.5,3.6,29.7L3.6,29.7z" />
|
||||
</symbol>
|
||||
<symbol id="move" viewBox="0 0 16 16">
|
||||
<path d="M8 0.499878L5.5 2.99988L6.205 3.70488L7.5 2.41488V6.49988C7.5 6.77602 7.72386 6.99988 8 6.99988C8.27614 6.99988 8.5 6.77602 8.5 6.49988V2.41488L9.795 3.70488L10.5 2.99988L8 0.499878Z"/>
|
||||
<path d="M9.795 12.2949L8.5 13.5849V9.49988C8.5 9.22374 8.27614 8.99988 8 8.99988C7.72386 8.99988 7.5 9.22374 7.5 9.49988V13.5849L6.205 12.2949L5.5 12.9999L8 15.4999L10.5 12.9999L9.795 12.2949Z"/>
|
||||
<path d="M0.5 7.99988L3 10.4999L3.705 9.79488L2.415 8.49988H6.5C6.77614 8.49988 7 8.27602 7 7.99988C7 7.72374 6.77614 7.49988 6.5 7.49988H2.415L3.705 6.20488L3 5.49988L0.5 7.99988Z"/>
|
||||
<path d="M12.295 6.20488L13.585 7.49988H9.5C9.22386 7.49988 9 7.72374 9 7.99988C9 8.27602 9.22386 8.49988 9.5 8.49988H13.585L12.295 9.79488L13 10.4999L15.5 7.99988L13 5.49988L12.295 6.20488Z"/>
|
||||
</symbol>
|
||||
</svg>
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 23 KiB |
@ -52,6 +52,7 @@
|
||||
"Table": "Table",
|
||||
"InsertTable": "Insert table",
|
||||
"TableOptions": "Customize table",
|
||||
"SelectTable": "Select table",
|
||||
"Width": "Width",
|
||||
"Height": "Height",
|
||||
"Unset": "Unset",
|
||||
|
@ -52,6 +52,7 @@
|
||||
"Table": "Таблица",
|
||||
"InsertTable": "Добавить таблицу",
|
||||
"TableOptions": "Настроить таблицу",
|
||||
"SelectTable": "Выделить таблицу",
|
||||
"Width": "Ширина",
|
||||
"Height": "Высота",
|
||||
"Unset": "Убрать",
|
||||
|
@ -40,5 +40,6 @@ loadMetadata(textEditor.icon, {
|
||||
ScaleOut: `${icons}#scaleOut`,
|
||||
Download: `${icons}#download`,
|
||||
Note: `${icons}#note`,
|
||||
Comment: `${icons}#comment`
|
||||
Comment: `${icons}#comment`,
|
||||
SelectTable: `${icons}#move`
|
||||
})
|
||||
|
@ -23,6 +23,7 @@
|
||||
export let size: IconSize
|
||||
export let editor: Editor
|
||||
export let actionCtx: ActionContext
|
||||
export let blockMouseEvents = true
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
let selected: boolean = false
|
||||
@ -44,6 +45,11 @@
|
||||
}
|
||||
|
||||
async function handleClick (event: MouseEvent): Promise<void> {
|
||||
if (blockMouseEvents) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
}
|
||||
|
||||
const handler = action.action
|
||||
|
||||
if (typeof handler === 'string') {
|
||||
@ -67,7 +73,7 @@
|
||||
use:tooltip={{ label: action.label }}
|
||||
tabindex="0"
|
||||
data-id={'btn' + action.label.split(':').pop()}
|
||||
on:click|preventDefault|stopPropagation={handleClick}
|
||||
on:click={handleClick}
|
||||
>
|
||||
<Icon icon={action.icon} {size} />
|
||||
</button>
|
||||
|
@ -20,6 +20,7 @@
|
||||
import { NodeViewContent, NodeViewProps, NodeViewWrapper } from '../../node-view'
|
||||
import { findTable, insertColumn, insertRow } from './utils'
|
||||
import { TableMap } from '@tiptap/pm/tables'
|
||||
import TableToolbar from './TableToolbar.svelte'
|
||||
|
||||
export let node: NodeViewProps['node']
|
||||
export let getPos: NodeViewProps['getPos']
|
||||
@ -77,6 +78,10 @@
|
||||
</table>
|
||||
|
||||
{#if editable && focused}
|
||||
<div class="table-toolbar-container" contenteditable="false">
|
||||
<TableToolbar {editor} />
|
||||
</div>
|
||||
|
||||
<!-- add col button -->
|
||||
<div class="table-button-container table-button-container__col flex" contenteditable="false">
|
||||
<div class="w-full h-full flex showOnHover">
|
||||
@ -115,41 +120,14 @@
|
||||
right: 0;
|
||||
}
|
||||
|
||||
&.table-selected {
|
||||
&::before {
|
||||
border: 1.25rem var(--theme-button-default) solid;
|
||||
border-radius: 1.25rem;
|
||||
inset: 0 -1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.table-button-container {
|
||||
position: absolute;
|
||||
transition: opacity 0.15s ease-in-out 0.15s;
|
||||
|
||||
&__col {
|
||||
right: -1.25rem;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: 1.25rem 0;
|
||||
|
||||
.table-button {
|
||||
width: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__row {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
.table-button {
|
||||
height: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.table-button {
|
||||
border-radius: 2px;
|
||||
background-color: transparent;
|
||||
color: var(--theme-button-contrast-hovered);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--theme-button-hovered);
|
||||
@ -161,6 +139,7 @@
|
||||
height: 0.25rem;
|
||||
border-radius: 50%;
|
||||
background-color: var(--text-editor-table-marker-color);
|
||||
display: none;
|
||||
}
|
||||
|
||||
.table-button__icon {
|
||||
@ -175,6 +154,38 @@
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&__col {
|
||||
right: -1.25rem;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: 1.25rem 0;
|
||||
|
||||
.table-button {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
width: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__row {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
.table-button {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
height: 1.25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-toolbar-container {
|
||||
position: absolute;
|
||||
top: -1.5rem;
|
||||
right: 0;
|
||||
z-index: 200;
|
||||
}
|
||||
</style>
|
||||
|
@ -0,0 +1,71 @@
|
||||
<!--
|
||||
//
|
||||
// Copyright © 2023, 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { NodeViewProps } from '../../node-view'
|
||||
import textEditor, { ActionContext, TextEditorAction } from '@hcengineering/text-editor'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import TextActionButton from '../../TextActionButton.svelte'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
|
||||
export let editor: NodeViewProps['editor']
|
||||
|
||||
const actionsQuery = createQuery()
|
||||
const actionCtx: ActionContext = {
|
||||
mode: 'full',
|
||||
tag: 'table-toolbar'
|
||||
}
|
||||
|
||||
let actions: TextEditorAction[] = []
|
||||
|
||||
async function updateActions (newActions: TextEditorAction[], ctx: ActionContext): Promise<void> {
|
||||
const out: TextEditorAction[] = []
|
||||
for (const action of newActions) {
|
||||
const tester = action.visibilityTester
|
||||
|
||||
if (tester === undefined) {
|
||||
out.push(action)
|
||||
continue
|
||||
}
|
||||
|
||||
const testerFunc = await getResource(tester)
|
||||
if (await testerFunc(editor, ctx)) {
|
||||
out.push(action)
|
||||
}
|
||||
}
|
||||
|
||||
actions = out
|
||||
}
|
||||
|
||||
$: actionsQuery.query(textEditor.class.TextEditorAction, { category: 70 }, (result) => {
|
||||
void updateActions([...result], actionCtx)
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="table-toolbar flex" contenteditable="false">
|
||||
{#each actions as action}
|
||||
<TextActionButton {action} {editor} size="small" {actionCtx} blockMouseEvents={false} />
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.table-toolbar {
|
||||
padding: 0.25rem;
|
||||
background-color: var(--theme-comp-header-color);
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: var(--button-shadow);
|
||||
}
|
||||
</style>
|
@ -138,7 +138,7 @@ const handleMouseDown = (
|
||||
const dropMarkerLeftPx =
|
||||
dropIndex <= col ? columns[dropIndex].leftPx : columns[dropIndex].leftPx + columns[dropIndex].widthPx
|
||||
|
||||
updateColDropMarker(dropMarker, dropMarkerLeftPx - dropMarkerWidthPx / 2, dropMarkerWidthPx)
|
||||
updateColDropMarker(dropMarker, dropMarkerLeftPx - Math.floor(dropMarkerWidthPx / 2) - 1, dropMarkerWidthPx)
|
||||
updateColDragMarker(dragMarker, dragMarkerLeftPx, dragMarkerWidthPx)
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ export const dropMarkerId = 'table-drop-marker'
|
||||
export const colDragMarkerId = 'table-col-drag-marker'
|
||||
export const rowDragMarkerId = 'table-row-drag-marker'
|
||||
|
||||
export const dropMarkerWidthPx = 2
|
||||
export const dropMarkerWidthPx = 1
|
||||
|
||||
export const tableDragMarkerDecoration = (state: EditorState, table: TableNodeLocation): Decoration[] => {
|
||||
const dropMarker = document.createElement('div')
|
||||
@ -33,14 +33,16 @@ export const tableDragMarkerDecoration = (state: EditorState, table: TableNodeLo
|
||||
const colDragMarker = document.createElement('div')
|
||||
colDragMarker.id = colDragMarkerId
|
||||
colDragMarker.classList.add('table-col-drag-marker')
|
||||
colDragMarker.innerHTML = handleSvg
|
||||
colDragMarker.style.display = 'none'
|
||||
const colDragMarkerBtn = colDragMarker.appendChild(document.createElement('button'))
|
||||
colDragMarkerBtn.innerHTML = handleSvg
|
||||
|
||||
const rowDragMarker = document.createElement('div')
|
||||
rowDragMarker.id = rowDragMarkerId
|
||||
rowDragMarker.classList.add('table-row-drag-marker')
|
||||
rowDragMarker.innerHTML = handleSvg
|
||||
rowDragMarker.style.display = 'none'
|
||||
const rowDragMarkerBtn = rowDragMarker.appendChild(document.createElement('button'))
|
||||
rowDragMarkerBtn.innerHTML = handleSvg
|
||||
|
||||
return [
|
||||
Decoration.widget(table.start, dropMarker),
|
||||
|
@ -17,11 +17,11 @@ import { type Editor } from '@tiptap/core'
|
||||
import TiptapTable from '@tiptap/extension-table'
|
||||
import { CellSelection } from '@tiptap/pm/tables'
|
||||
import { getEventPositionElement, SelectPopup, showPopup } from '@hcengineering/ui'
|
||||
import textEditor from '@hcengineering/text-editor'
|
||||
import textEditor, { type ActionContext } from '@hcengineering/text-editor'
|
||||
|
||||
import { SvelteNodeViewRenderer } from '../../node-view'
|
||||
import TableNodeView from './TableNodeView.svelte'
|
||||
import { isTableSelected } from './utils'
|
||||
import { findTable, isTableSelected, selectTable as selectTableNode } from './utils'
|
||||
import AddColAfter from '../../icons/table/AddColAfter.svelte'
|
||||
import AddColBefore from '../../icons/table/AddColBefore.svelte'
|
||||
import AddRowAfter from '../../icons/table/AddRowAfter.svelte'
|
||||
@ -31,6 +31,8 @@ import DeleteRow from '../../icons/table/DeleteRow.svelte'
|
||||
import DeleteTable from '../../icons/table/DeleteTable.svelte'
|
||||
|
||||
export const Table = TiptapTable.extend({
|
||||
draggable: true,
|
||||
|
||||
addKeyboardShortcuts () {
|
||||
return {
|
||||
'Mod-Backspace': () => handleDelete(this.editor),
|
||||
@ -145,6 +147,19 @@ export async function openTableOptions (editor: Editor, event: MouseEvent): Prom
|
||||
})
|
||||
}
|
||||
|
||||
export async function selectTable (editor: Editor, event: MouseEvent): Promise<void> {
|
||||
const table = findTable(editor.state.selection)
|
||||
if (table === undefined) return
|
||||
|
||||
event.preventDefault()
|
||||
|
||||
editor.view.dispatch(selectTableNode(table, editor.state.tr))
|
||||
}
|
||||
|
||||
export async function isEditableTableActive (editor: Editor): Promise<boolean> {
|
||||
return editor.isEditable && editor.isActive('table')
|
||||
}
|
||||
|
||||
export async function isTableToolbarContext (editor: Editor, context: ActionContext): Promise<boolean> {
|
||||
return editor.isEditable && editor.isActive('table') && context.tag === 'table-toolbar'
|
||||
}
|
||||
|
@ -110,6 +110,13 @@ class SvelteNodeView extends NodeView<SvelteNodeViewComponent, Editor, SvelteNod
|
||||
return this.contentDOMElement
|
||||
}
|
||||
|
||||
override stopEvent (event: Event): boolean {
|
||||
if (typeof this.options.stopEvent === 'function') {
|
||||
return this.options.stopEvent({ event })
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
update (node: ProseMirrorNode, decorations: DecorationWithType[]): boolean {
|
||||
if (typeof this.options.update === 'function') {
|
||||
return this.options.update(node, decorations)
|
||||
|
@ -17,7 +17,12 @@
|
||||
import { type Resources } from '@hcengineering/platform'
|
||||
import { formatLink } from './kits/default-kit'
|
||||
import { isEditable, isHeadingVisible } from './kits/editor-kit'
|
||||
import { openTableOptions, isEditableTableActive } from './components/extension/table/table'
|
||||
import {
|
||||
openTableOptions,
|
||||
isEditableTableActive,
|
||||
isTableToolbarContext,
|
||||
selectTable
|
||||
} from './components/extension/table/table'
|
||||
import { openImage, downloadImage, expandImage, moreImageActions } from './components/extension/imageExt'
|
||||
import { configureNote, isEditableNote } from './components/extension/note'
|
||||
import { createInlineComment, shouldShowCreateInlineCommentAction } from './components/extension/inlineComment'
|
||||
@ -81,12 +86,14 @@ export default async (): Promise<Resources> => ({
|
||||
function: {
|
||||
FormatLink: formatLink,
|
||||
OpenTableOptions: openTableOptions,
|
||||
SelectTable: selectTable,
|
||||
OpenImage: openImage,
|
||||
ExpandImage: expandImage,
|
||||
DownloadImage: downloadImage,
|
||||
MoreImageActions: moreImageActions,
|
||||
ConfigureNote: configureNote,
|
||||
IsEditableTableActive: isEditableTableActive,
|
||||
IsTableToolbarContext: isTableToolbarContext,
|
||||
IsEditableNote: isEditableNote,
|
||||
IsEditable: isEditable,
|
||||
IsHeadingVisible: isHeadingVisible,
|
||||
|
@ -91,6 +91,7 @@ export default plugin(textEditorId, {
|
||||
CategoryColumn: '' as IntlString,
|
||||
Table: '' as IntlString,
|
||||
TableOptions: '' as IntlString,
|
||||
SelectTable: '' as IntlString,
|
||||
Width: '' as IntlString,
|
||||
Height: '' as IntlString,
|
||||
Unset: '' as IntlString,
|
||||
@ -123,6 +124,7 @@ export default plugin(textEditorId, {
|
||||
ScaleOut: '' as Asset,
|
||||
Download: '' as Asset,
|
||||
Note: '' as Asset,
|
||||
Comment: '' as Asset
|
||||
Comment: '' as Asset,
|
||||
SelectTable: '' as Asset
|
||||
}
|
||||
})
|
||||
|
@ -108,6 +108,7 @@ export interface ActionContext {
|
||||
objectId?: Ref<Doc>
|
||||
objectClass?: Ref<Class<Doc>>
|
||||
objectSpace?: Ref<Space>
|
||||
tag?: string
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -32,8 +32,7 @@ export class DocumentContentPage extends CommonPage {
|
||||
readonly buttonInsertColumn = (col: number = 0): Locator =>
|
||||
this.page.locator('div.table-col-insert').nth(col).locator('button')
|
||||
|
||||
readonly buttonInsertLastRow = (): Locator =>
|
||||
this.page.locator('table.proseTable + div.table-button-container__col + div.table-button-container__row')
|
||||
readonly buttonInsertLastRow = (): Locator => this.page.locator('div.table-button-container__row')
|
||||
|
||||
readonly buttonInsertInnerRow = (row: number = 0): Locator =>
|
||||
this.page.locator('table.proseTable').locator('tr').nth(row).locator('div.table-row-insert button')
|
||||
|
Loading…
Reference in New Issue
Block a user