feat: create table plugin

This commit is contained in:
0xzion
2023-10-01 00:02:47 +08:00
parent 30bb341534
commit 8616d577ba
29 changed files with 318 additions and 25 deletions

View File

@@ -46,6 +46,7 @@ const config = {
'@penx/image',
'@penx/link',
'@penx/internal-link',
'@penx/table',
'uikit',
'slate-lists',
'easy-modal',

View File

@@ -1,4 +1,3 @@
import blockSelector from './plugins/block-selector'
import table from './plugins/table'
export const docPluginList = [blockSelector(), table()]
export const docPluginList = [blockSelector()]

View File

@@ -32,6 +32,7 @@
"@penx/code-block": "workspace:*",
"@penx/image": "workspace:*",
"@penx/link": "workspace:*",
"@penx/table": "workspace:*",
"@penx/internal-link": "workspace:*",
"@penx/store": "workspace:*",
"immer": "^10.0.2",

View File

@@ -12,6 +12,7 @@ import * as list from '@penx/list'
import { db } from '@penx/local-db'
import * as paragraph from '@penx/paragraph'
import * as storageEstimate from '@penx/storage-estimate'
import * as table from '@penx/table'
import * as wordCount from '@penx/word-count'
const builtins = [
@@ -29,6 +30,7 @@ const builtins = [
{ id: 'image', activate: image.activate },
{ id: 'link', activate: link.activate },
// { id: 'internal-link', activate: internalLink.activate },
{ id: 'table', activate: table.activate },
]
export class PluginLoader {

View File

@@ -0,0 +1,44 @@
import { BaseEditor, BaseElement } from 'slate'
import { ReactEditor } from 'slate-react'
export enum ElementType {
table = 'table',
tr = 'tr',
td = 'td',
th = 'th',
}
export interface BaseCustomElement extends BaseElement {
id?: string
}
export interface TableElement extends BaseCustomElement {
type: ElementType.table
colWidths: number[] // table col widths
isHeaderRow: boolean
isHeaderColumn: boolean
children: TableRowElement[]
}
export interface TableRowElement extends BaseCustomElement {
type: ElementType.tr
children: TableCellElement[]
}
export interface TableCellElement extends BaseCustomElement {
type: ElementType.td
}
export type CustomEditor = BaseEditor &
ReactEditor & {
id?: string
}
export type CustomElement = TableElement | TableRowElement | TableCellElement
declare module 'slate' {
interface CustomTypes {
Editor: CustomEditor
Element: CustomElement
}
}

View File

@@ -0,0 +1,50 @@
{
"name": "@penx/table",
"version": "0.0.0",
"main": "./src/index.ts",
"types": "./src/index.ts",
"scripts": {
"lint": "eslint \"**/*.ts*\""
},
"devDependencies": {
"@types/lodash": "^4.14.195",
"@types/node": "^20.7.2",
"@types/react": "^18.2.22",
"@types/react-dom": "^18.2.7",
"eslint": "^8.42.0",
"eslint-config-custom": "workspace:*",
"react": "^18.2.0",
"tsconfig": "workspace:*",
"typescript": "^5.1.3"
},
"dependencies": {
"@bone-ui/iconify": "^0.37.0",
"@bone-ui/icons": "^0.37.0",
"@bone-ui/utils": "^0.37.0",
"@dnd-kit/core": "^6.0.8",
"@dnd-kit/sortable": "^7.0.2",
"@dnd-kit/utilities": "^3.1.0",
"@floating-ui/react": "^0.25.4",
"@fower/react": "^1.86.0",
"@penx/constants": "workspace:*",
"@penx/editor-queries": "workspace:*",
"@penx/editor-shared": "workspace:*",
"@penx/editor-transforms": "workspace:*",
"@penx/editor-types": "workspace:*",
"@penx/hooks": "workspace:*",
"@penx/icons": "workspace:*",
"@penx/local-db": "workspace:*",
"@penx/plugin-typings": "*",
"@penx/shared": "workspace:*",
"bone-ui": "^0.37.0",
"framer-motion": "^10.12.16",
"immer": "^10.0.2",
"lodash": "^4.17.21",
"mitt": "^3.0.0",
"slate": "0.94.1",
"slate-history": "0.93.0",
"slate-lists": "workspace:*",
"slate-react": "^0.98.3",
"uikit": "workspace:*"
}
}

View File

@@ -0,0 +1,12 @@
import { getState, useStore } from 'stook'
const key = 'active-element'
export function useActiveElement(id = '') {
const [activeId, setActiveId] = useStore<string | null>(key + id, null)
return { activeId, setActiveId }
}
export function getActiveRow(id = ''): boolean {
return getState(key + id)
}

View File

@@ -1,14 +1,15 @@
import { TableCellsOutline } from '@bone-ui/icons'
import { ElementType } from '@penx/editor-shared'
import { EditorPlugin } from '@penx/editor-types'
import { PluginContext } from '@penx/plugin-typings'
import { getEmptyTableNode } from './getEmptyTableNode'
import { Table } from './ui/Table/Table'
import { TableCell } from './ui/TableCell'
import { TableRow } from './ui/TableRow'
import { withTable } from './withTable'
export default function table(): EditorPlugin {
return {
export function activate(ctx: PluginContext) {
ctx.registerBlock({
with: withTable,
elements: [
{
@@ -28,5 +29,5 @@ export default function table(): EditorPlugin {
component: TableCell,
},
],
}
})
}

View File

@@ -1,16 +1,16 @@
import { produce } from 'immer'
import { Editor, Node, Path, Transforms } from 'slate'
import { findNodePath } from '@penx/editor-queries'
import {
TableCellElement,
TableElement,
TableRowElement,
} from '@penx/editor-types'
import { getEmptyCellNode } from '../getEmptyCellNode'
import { getEmptyRowNode } from '../getEmptyRowNode'
} from '../../custom-types'
export class CellNode {
constructor(private editor: Editor, private element: TableCellElement) {}
constructor(
private editor: Editor,
private element: TableCellElement,
) {}
get path() {
return findNodePath(this.editor, this.element) || []

View File

@@ -1,12 +1,15 @@
import { produce } from 'immer'
import { Editor, Path, Transforms } from 'slate'
import { findNodePath } from '@penx/editor-queries'
import { TableCellElement, TableElement } from '@penx/editor-types'
import { TableCellElement, TableElement } from '../../custom-types'
import { getEmptyCellNode } from '../getEmptyCellNode'
import { getEmptyRowNode } from '../getEmptyRowNode'
export class TableNode {
constructor(private editor: Editor, private element: TableElement) {}
constructor(
private editor: Editor,
private element: TableElement,
) {}
get firstRowElement() {
return this.element.children[0]

View File

@@ -1,7 +1,7 @@
import { PlusOutline } from '@bone-ui/icons'
import { Box } from '@fower/react'
import { useSlate, useSlateStatic } from 'slate-react'
import { TableElement } from '@penx/editor-types'
import { TableElement } from '../../../custom-types'
import { TableNode } from '../../nodes/TableNode'
interface Props {

View File

@@ -1,7 +1,7 @@
import { PlusOutline } from '@bone-ui/icons'
import { Box } from '@fower/react'
import { useSlate, useSlateStatic } from 'slate-react'
import { TableElement } from '@penx/editor-types'
import { TableElement } from '../../../custom-types'
import { TableNode } from '../../nodes/TableNode'
interface Props {

View File

@@ -1,4 +1,4 @@
import { TableElement } from '@penx/editor-types'
import { TableElement } from '../../../custom-types'
import { DraglineItem } from './DraglineItem'
export const DraglineList = ({ element }: { element: TableElement }) => {

View File

@@ -1,5 +1,4 @@
import { useState } from 'react'
import { PlusOutline, PlusSolid } from '@bone-ui/icons'
import {
closestCenter,
DndContext,
@@ -23,7 +22,7 @@ import { Transforms } from 'slate'
import { useSlate, useSlateStatic } from 'slate-react'
import { findNode, findNodePath } from '@penx/editor-queries'
import { ElementProps, TableElement } from '@penx/editor-types'
import { useActiveElement } from '../../../../stores/activeElement.store'
import { useActiveElement } from '../../activeElement.store'
import { AddColumnBar } from './AddColumnBar'
import { AddRowBar } from './AddRowBar'
import { DraglineList } from './DraglineList'

View File

@@ -1,11 +1,17 @@
import { EllipsisHorizontalOutline } from '@bone-ui/icons'
import { Menu, MenuItem } from '@bone-ui/menu'
import { Box } from '@fower/react'
import { Element, Transforms } from 'slate'
import { ReactEditor, useSelected, useSlate, useSlateStatic } from 'slate-react'
import { Popover, PopoverContent, PopoverTrigger, Switch } from 'uikit'
import {
Menu,
MenuItem,
Popover,
PopoverContent,
PopoverTrigger,
Switch,
} from 'uikit'
import { findNodePath } from '@penx/editor-queries'
import { TableElement } from '@penx/editor-types'
import { TableElement } from '../../../custom-types'
export const TableOptions = ({ element }: { element: TableElement }) => {
const editor = useSlateStatic()

View File

@@ -3,14 +3,15 @@ import { Box, CSSObject } from '@fower/react'
import { motion, useMotionValue, useTransform } from 'framer-motion'
import { useSlate, useSlateStatic } from 'slate-react'
import { Popover, PopoverContent, PopoverTrigger } from 'uikit'
import { ElementProps, TableCellElement } from '@penx/editor-types'
import { IconInsertBottom } from '../../../components/icons/IconInsertBottom'
import { IconInsertLeft } from '../../../components/icons/IconInsertLeft'
import { IconInsertRight } from '../../../components/icons/IconInsertRight'
import { IconInsertTop } from '../../../components/icons/IconInsertTop'
import { ElementProps } from '@penx/plugin-typings'
import { TableCellElement } from '../../custom-types'
import { CellNode } from '../nodes/CellNode'
import { TableNode } from '../nodes/TableNode'
import { useRowSortable } from '../rowSortable.store'
import { IconInsertBottom } from './icons/IconInsertBottom'
import { IconInsertLeft } from './icons/IconInsertLeft'
import { IconInsertRight } from './icons/IconInsertRight'
import { IconInsertTop } from './icons/IconInsertTop'
export const TableCell = ({
attributes,

View File

@@ -0,0 +1,12 @@
import { iconify } from '@bone-ui/iconify'
export const IconInsertBottom = iconify({
displayName: 'IconInsertBottom',
viewBox: '0 0 24 24',
fill: 'currentColor',
atomicProps: {
square: 24,
cursorPointer: true,
},
d: 'M10.875,7.25 L10.875,4.5 C10.875,3.94771525 11.3227153,3.5 11.875,3.5 L12.125,3.5 C12.6772847,3.5 13.125,3.94771525 13.125,4.5 L13.125,7.25 L15.5,7.25 C16.0522847,7.25 16.5,7.69771525 16.5,8.25 L16.5,8.75 C16.5,9.30228475 16.0522847,9.75 15.5,9.75 L13.125,9.75 L13.125,12.5 C13.125,13.0522847 12.6772847,13.5 12.125,13.5 L11.875,13.5 C11.3227153,13.5 10.875,13.0522847 10.875,12.5 L10.875,9.75 L8.5,9.75 C7.94771525,9.75 7.5,9.30228475 7.5,8.75 L7.5,8.25 C7.5,7.69771525 7.94771525,7.25 8.5,7.25 L10.875,7.25 Z M16.2807612,13.8417088 C16.7884428,13.3860971 17.6115572,13.3860971 18.1192388,13.8417088 C18.6269204,14.2973204 18.6269204,15.0360129 18.1192388,15.4916246 L12.9192388,20.1582912 C12.4115572,20.6139029 11.5884428,20.6139029 11.0807612,20.1582912 L5.88076118,15.4916246 C5.37307961,15.0360129 5.37307961,14.2973204 5.88076118,13.8417088 C6.38844276,13.3860971 7.21155724,13.3860971 7.71923882,13.8417088 L12,17.6834175 L16.2807612,13.8417088 Z',
})

View File

@@ -0,0 +1,12 @@
import { iconify } from '@bone-ui/iconify'
export const IconInsertLeft = iconify({
displayName: 'IconInsertLeft',
viewBox: '0 0 24 24',
fill: 'currentColor',
atomicProps: {
square: 24,
cursorPointer: true,
},
d: 'M16.625,10.75 L19,10.75 C19.5522847,10.75 20,11.1977153 20,11.75 L20,12.25 C20,12.8022847 19.5522847,13.25 19,13.25 L16.625,13.25 L16.625,16 C16.625,16.5522847 16.1772847,17 15.625,17 L15.375,17 C14.8227153,17 14.375,16.5522847 14.375,16 L14.375,13.25 L12,13.25 C11.4477153,13.25 11,12.8022847 11,12.25 L11,11.75 C11,11.1977153 11.4477153,10.75 12,10.75 L14.375,10.75 L14.375,8 C14.375,7.44771525 14.8227153,7 15.375,7 L15.625,7 C16.1772847,7 16.625,7.44771525 16.625,8 L16.625,10.75 Z M10.6582912,15.9514719 C11.1139029,16.420101 11.1139029,17.179899 10.6582912,17.6485281 C10.2026796,18.1171573 9.4639871,18.1171573 9.00837542,17.6485281 L4.34170876,12.8485281 C3.88609708,12.379899 3.88609708,11.620101 4.34170876,11.1514719 L9.00837542,6.35147186 C9.4639871,5.88284271 10.2026796,5.88284271 10.6582912,6.35147186 C11.1139029,6.82010101 11.1139029,7.57989899 10.6582912,8.04852814 L6.81658249,12 L10.6582912,15.9514719 Z',
})

View File

@@ -0,0 +1,12 @@
import { iconify } from '@bone-ui/iconify'
export const IconInsertRight = iconify({
displayName: 'IconInsertRight',
viewBox: '0 0 24 24',
fill: 'currentColor',
atomicProps: {
square: 24,
cursorPointer: true,
},
d: 'M7.375,13.25 L5,13.25 C4.44771525,13.25 4,12.8022847 4,12.25 L4,11.75 C4,11.1977153 4.44771525,10.75 5,10.75 L7.375,10.75 L7.375,8 C7.375,7.44771525 7.82271525,7 8.375,7 L8.625,7 C9.17728475,7 9.625,7.44771525 9.625,8 L9.625,10.75 L12,10.75 C12.5522847,10.75 13,11.1977153 13,11.75 L13,12.25 C13,12.8022847 12.5522847,13.25 12,13.25 L9.625,13.25 L9.625,16 C9.625,16.5522847 9.17728475,17 8.625,17 L8.375,17 C7.82271525,17 7.375,16.5522847 7.375,16 L7.375,13.25 Z M13.3417088,8.04852814 C12.8860971,7.57989899 12.8860971,6.82010101 13.3417088,6.35147186 C13.7973204,5.88284271 14.5360129,5.88284271 14.9916246,6.35147186 L19.6582912,11.1514719 C20.1139029,11.620101 20.1139029,12.379899 19.6582912,12.8485281 L14.9916246,17.6485281 C14.5360129,18.1171573 13.7973204,18.1171573 13.3417088,17.6485281 C12.8860971,17.179899 12.8860971,16.420101 13.3417088,15.9514719 L17.1834175,12 L13.3417088,8.04852814 Z',
})

View File

@@ -0,0 +1,12 @@
import { iconify } from '@bone-ui/iconify'
export const IconInsertTop = iconify({
displayName: 'IconInsertTop',
viewBox: '0 0 24 24',
fill: 'currentColor',
atomicProps: {
square: 24,
cursorPointer: true,
},
d: 'M13.125,16.75 L13.125,19.5 C13.125,20.0522847 12.6772847,20.5 12.125,20.5 L11.875,20.5 C11.3227153,20.5 10.875,20.0522847 10.875,19.5 L10.875,16.75 L8.5,16.75 C7.94771525,16.75 7.5,16.3022847 7.5,15.75 L7.5,15.25 C7.5,14.6977153 7.94771525,14.25 8.5,14.25 L10.875,14.25 L10.875,11.5 C10.875,10.9477153 11.3227153,10.5 11.875,10.5 L12.125,10.5 C12.6772847,10.5 13.125,10.9477153 13.125,11.5 L13.125,14.25 L15.5,14.25 C16.0522847,14.25 16.5,14.6977153 16.5,15.25 L16.5,15.75 C16.5,16.3022847 16.0522847,16.75 15.5,16.75 L13.125,16.75 Z M7.38994949,10.6094757 C6.84321549,11.1301748 5.95678451,11.1301748 5.41005051,10.6094757 C4.8633165,10.0887767 4.8633165,9.24455668 5.41005051,8.72385763 L11.0100505,3.39052429 C11.5567845,2.86982524 12.4432155,2.86982524 12.9899495,3.39052429 L18.5899495,8.72385763 C19.1366835,9.24455668 19.1366835,10.0887767 18.5899495,10.6094757 C18.0432155,11.1301748 17.1567845,11.1301748 16.6100505,10.6094757 L12,6.21895142 L7.38994949,10.6094757 Z',
})

View File

@@ -0,0 +1,8 @@
{
"extends": "tsconfig/react-library.json",
"compilerOptions": {
"lib": ["ESNext", "DOM"]
},
"include": [".", "custom-types.ts"],
"exclude": ["dist", "build", "node_modules"]
}

118
pnpm-lock.yaml generated
View File

@@ -1944,6 +1944,9 @@ importers:
'@penx/store':
specifier: workspace:*
version: link:../store
'@penx/table':
specifier: workspace:*
version: link:../../plugins/table
'@penx/word-count':
specifier: workspace:*
version: link:../../plugins/word-count
@@ -3171,6 +3174,121 @@ importers:
specifier: ^5.1.3
version: 5.2.2
plugins/table:
dependencies:
'@bone-ui/iconify':
specifier: ^0.37.0
version: 0.37.0
'@bone-ui/icons':
specifier: ^0.37.0
version: 0.37.0
'@bone-ui/utils':
specifier: ^0.37.0
version: 0.37.0
'@dnd-kit/core':
specifier: ^6.0.8
version: 6.0.8(react-dom@18.2.0)(react@18.2.0)
'@dnd-kit/sortable':
specifier: ^7.0.2
version: 7.0.2(@dnd-kit/core@6.0.8)(react@18.2.0)
'@dnd-kit/utilities':
specifier: ^3.1.0
version: 3.2.1(react@18.2.0)
'@floating-ui/react':
specifier: ^0.25.4
version: 0.25.4(react-dom@18.2.0)(react@18.2.0)
'@fower/react':
specifier: ^1.86.0
version: 1.86.0
'@penx/constants':
specifier: workspace:*
version: link:../../packages/constants
'@penx/editor-queries':
specifier: workspace:*
version: link:../../packages/editor-queries
'@penx/editor-shared':
specifier: workspace:*
version: link:../../packages/editor-shared
'@penx/editor-transforms':
specifier: workspace:*
version: link:../../packages/editor-transforms
'@penx/editor-types':
specifier: workspace:*
version: link:../../packages/editor-types
'@penx/hooks':
specifier: workspace:*
version: link:../../packages/hooks
'@penx/icons':
specifier: workspace:*
version: link:../../packages/icons
'@penx/local-db':
specifier: workspace:*
version: link:../../packages/local-db
'@penx/plugin-typings':
specifier: '*'
version: link:../../packages/plugin-typings
'@penx/shared':
specifier: workspace:*
version: link:../../packages/shared
bone-ui:
specifier: ^0.37.0
version: 0.37.0(@types/react@18.2.22)(react-dom@18.2.0)(react@18.2.0)
framer-motion:
specifier: ^10.12.16
version: 10.16.4(react-dom@18.2.0)(react@18.2.0)
immer:
specifier: ^10.0.2
version: 10.0.2
lodash:
specifier: ^4.17.21
version: 4.17.21
mitt:
specifier: ^3.0.0
version: 3.0.1
slate:
specifier: 0.94.1
version: 0.94.1
slate-history:
specifier: 0.93.0
version: 0.93.0(slate@0.94.1)
slate-lists:
specifier: workspace:*
version: link:../../packages/slate-lists
slate-react:
specifier: ^0.98.3
version: 0.98.4(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1)
uikit:
specifier: workspace:*
version: link:../../packages/uikit
devDependencies:
'@types/lodash':
specifier: ^4.14.195
version: 4.14.198
'@types/node':
specifier: ^20.7.2
version: 20.7.2
'@types/react':
specifier: ^18.2.22
version: 18.2.22
'@types/react-dom':
specifier: ^18.2.7
version: 18.2.7
eslint:
specifier: ^8.42.0
version: 8.49.0
eslint-config-custom:
specifier: workspace:*
version: link:../../packages/eslint-config-custom
react:
specifier: ^18.2.0
version: 18.2.0
tsconfig:
specifier: workspace:*
version: link:../../packages/tsconfig
typescript:
specifier: ^5.1.3
version: 5.2.2
plugins/word-count:
dependencies:
'@fower/react':