mirror of
https://github.com/penxio/penx.git
synced 2026-04-19 03:03:06 -04:00
feat: improve LiveQuery ui
This commit is contained in:
@@ -89,6 +89,7 @@ export const BlockSelectorContent = ({ close, element }: Props) => {
|
||||
|
||||
const next = Path.next(Path.parent(at))
|
||||
|
||||
// create new empty list item node
|
||||
Transforms.insertNodes(
|
||||
editor,
|
||||
ListsEditor.createListItemTextNode(editor, {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export const ELEMENT_DATABASE = 'database'
|
||||
export const ELEMENT_NODE_QUERY = 'node_query'
|
||||
export const ELEMENT_LIVE_QUERY = 'live_query'
|
||||
|
||||
export const FIRST_COL_WIDTH = 50
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { ELEMENT_DATABASE, ELEMENT_NODE_QUERY } from './constants'
|
||||
import { DatabaseElement, NodeQueryElement } from './types'
|
||||
import { ELEMENT_DATABASE, ELEMENT_LIVE_QUERY } from './constants'
|
||||
import { DatabaseElement, LiveQueryElement } from './types'
|
||||
|
||||
export function isDatabase(node: any): node is DatabaseElement {
|
||||
return node?.type === ELEMENT_DATABASE
|
||||
}
|
||||
|
||||
export function isNodeQuery(node: any): node is NodeQueryElement {
|
||||
return node?.type === ELEMENT_NODE_QUERY
|
||||
export function isLiveQuery(node: any): node is LiveQueryElement {
|
||||
return node?.type === ELEMENT_LIVE_QUERY
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { TableIcon } from 'lucide-react'
|
||||
import { ExtensionContext } from '@penx/extension-typings'
|
||||
import { db } from '@penx/local-db'
|
||||
import { ELEMENT_DATABASE, ELEMENT_NODE_QUERY } from './constants'
|
||||
import { ELEMENT_DATABASE, ELEMENT_LIVE_QUERY } from './constants'
|
||||
import { Database } from './ui/Database'
|
||||
import { NodeQuery } from './ui/NodeQuery'
|
||||
import { LiveQuery } from './ui/LiveQuery/LiveQuery'
|
||||
import { withDatabase } from './withDatabase'
|
||||
|
||||
export * from './guard'
|
||||
@@ -20,6 +20,7 @@ export function activate(ctx: ExtensionContext) {
|
||||
name: 'Database',
|
||||
icon: TableIcon,
|
||||
async beforeInvokeCommand(editor) {
|
||||
console.log('before.............')
|
||||
return db.createDatabase()
|
||||
},
|
||||
},
|
||||
@@ -27,10 +28,10 @@ export function activate(ctx: ExtensionContext) {
|
||||
|
||||
{
|
||||
isVoid: true,
|
||||
type: ELEMENT_NODE_QUERY,
|
||||
component: NodeQuery,
|
||||
type: ELEMENT_LIVE_QUERY,
|
||||
component: LiveQuery,
|
||||
slashCommand: {
|
||||
name: 'Node Query',
|
||||
name: 'Live Query',
|
||||
icon: TableIcon,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BaseElement } from 'slate'
|
||||
import { ELEMENT_DATABASE } from './constants'
|
||||
import { ELEMENT_DATABASE, ELEMENT_LIVE_QUERY } from './constants'
|
||||
|
||||
export interface BaseCustomElement extends BaseElement {
|
||||
id?: string
|
||||
@@ -11,10 +11,6 @@ export interface DatabaseElement extends BaseCustomElement {
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface NodeQueryElement extends BaseCustomElement {
|
||||
type: typeof ELEMENT_DATABASE
|
||||
colWidths: number[] // table col widths
|
||||
isHeaderRow: boolean
|
||||
isHeaderColumn: boolean
|
||||
databaseId: string
|
||||
export interface LiveQueryElement extends BaseCustomElement {
|
||||
type: typeof ELEMENT_LIVE_QUERY
|
||||
}
|
||||
|
||||
24
extensions/database/src/ui/LiveQuery/LiveQuery.tsx
Normal file
24
extensions/database/src/ui/LiveQuery/LiveQuery.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Box } from '@fower/react'
|
||||
import { Input } from 'uikit'
|
||||
import { ElementProps } from '@penx/extension-typings'
|
||||
import { LiveQueryElement } from '../../types'
|
||||
import { LiveQueryProvider } from './LiveQueryContext'
|
||||
|
||||
export const LiveQuery = ({
|
||||
attributes,
|
||||
element,
|
||||
children,
|
||||
}: ElementProps<LiveQueryElement>) => {
|
||||
const sql = '#bookmark'
|
||||
return (
|
||||
<Box flex-1 mb8 mt8 contentEditable={false} {...attributes}>
|
||||
<Box>
|
||||
<Input placeholder="#bookmark" defaultValue="#bookmark" />
|
||||
</Box>
|
||||
<LiveQueryProvider sql={sql}>
|
||||
<Box>Node Query...</Box>
|
||||
</LiveQueryProvider>
|
||||
{children}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
56
extensions/database/src/ui/LiveQuery/LiveQueryContext.tsx
Normal file
56
extensions/database/src/ui/LiveQuery/LiveQueryContext.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
import {
|
||||
createContext,
|
||||
PropsWithChildren,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { db } from '@penx/local-db'
|
||||
import { ICellNode, IColumnNode, INode, IRowNode, IViewNode } from '@penx/types'
|
||||
|
||||
export interface ILiveQueryContext {
|
||||
database: INode
|
||||
views: IViewNode[]
|
||||
columns: IColumnNode[]
|
||||
rows: IRowNode[]
|
||||
cells: ICellNode[]
|
||||
}
|
||||
|
||||
export const liveQueryContext = createContext<ILiveQueryContext>(
|
||||
{} as ILiveQueryContext,
|
||||
)
|
||||
|
||||
export function useLiveQueryContext() {
|
||||
return useContext(liveQueryContext)
|
||||
}
|
||||
|
||||
interface LiveQueryProviderProps {
|
||||
sql: string
|
||||
}
|
||||
|
||||
export const LiveQueryProvider = ({
|
||||
children,
|
||||
sql,
|
||||
}: PropsWithChildren<LiveQueryProviderProps>) => {
|
||||
const { Provider } = liveQueryContext
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [ctx, setCtx] = useState({} as ILiveQueryContext)
|
||||
console.log('gogoggo.......')
|
||||
|
||||
const loadLiveQuery = useCallback(async () => {
|
||||
console.log('sql:', sql)
|
||||
const database = await db.getDatabaseByName('bookmark')
|
||||
|
||||
// const data = await db.getLiveQuery(databaseId)
|
||||
// setLoading(false)
|
||||
// setCtx(data as any)
|
||||
}, [sql])
|
||||
|
||||
useEffect(() => {
|
||||
loadLiveQuery()
|
||||
}, [loadLiveQuery])
|
||||
|
||||
if (loading) return null
|
||||
return <Provider value={ctx}>{children}</Provider>
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import { Box } from '@fower/react'
|
||||
import { ElementProps } from '@penx/extension-typings'
|
||||
import { NodeQueryElement } from '../types'
|
||||
import { DatabaseProvider } from './DatabaseContext'
|
||||
|
||||
export const NodeQuery = ({
|
||||
attributes,
|
||||
element,
|
||||
children,
|
||||
}: ElementProps<NodeQueryElement>) => {
|
||||
const { databaseId } = element
|
||||
|
||||
return (
|
||||
<Box flex-1 mb8 mt8 contentEditable={false} {...attributes}>
|
||||
<Box>Node Query</Box>
|
||||
{children}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -35,10 +35,12 @@ function onEnterInTitle(editor: Editor) {
|
||||
|
||||
export const onKeyDown: OnKeyDown = (editor, e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault()
|
||||
const handled = onEnterInTitle(editor)
|
||||
|
||||
if (handled) return
|
||||
if (handled) {
|
||||
e.preventDefault()
|
||||
return
|
||||
}
|
||||
|
||||
const node = getCurrentNode(editor)!
|
||||
// TODO: handle any
|
||||
|
||||
@@ -19,9 +19,8 @@ interface Props {
|
||||
export const TagSelectorContent = ({ close, element }: Props) => {
|
||||
const editor = useEditorStatic()
|
||||
const { nodeList } = useNodes()
|
||||
const tagNames = nodeList.tagNodes
|
||||
.map((node) => node.props.tag!)
|
||||
.filter((i) => !!i)
|
||||
const tagNames = nodeList.tagNodes.map((node) => node.props.name!)
|
||||
// .filter((i) => !!i)
|
||||
|
||||
const filteredTypes = tagNames.filter((item) => {
|
||||
const q = Node.string(element).replace(/^#/, '').toLowerCase()
|
||||
@@ -30,6 +29,7 @@ export const TagSelectorContent = ({ close, element }: Props) => {
|
||||
})
|
||||
|
||||
const text = Node.string(element)
|
||||
const tagName = text.replace(/^#/, '')
|
||||
|
||||
const selectTag = useCallback(
|
||||
(tagName: any) => {
|
||||
@@ -56,13 +56,11 @@ export const TagSelectorContent = ({ close, element }: Props) => {
|
||||
const listItemIdPrefix = 'type-list-item-'
|
||||
|
||||
const { cursor } = useKeyDownList({
|
||||
onEnter: (cursor) => {
|
||||
console.log('enter.......xx')
|
||||
onEnter: async (cursor) => {
|
||||
if (!filteredTypes.length) {
|
||||
console.log('create tag......')
|
||||
|
||||
store.createTag(text)
|
||||
selectTag(text)
|
||||
console.log('create tag......:', tagName)
|
||||
await store.createDatabase(tagName)
|
||||
selectTag(tagName)
|
||||
return
|
||||
}
|
||||
selectTag(filteredTypes[cursor])
|
||||
|
||||
@@ -98,7 +98,7 @@ class DB {
|
||||
await this.node.insert(
|
||||
getNewNode({
|
||||
spaceId,
|
||||
type: NodeType.TAG_ROOT,
|
||||
type: NodeType.DATABASE_ROOT,
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -512,6 +512,20 @@ class DB {
|
||||
}
|
||||
}
|
||||
|
||||
getDatabaseByName = async (name: string) => {
|
||||
const space = await this.getActiveSpace()
|
||||
const nodes = await this.node.select({
|
||||
where: {
|
||||
type: NodeType.DATABASE,
|
||||
spaceId: space.id,
|
||||
},
|
||||
})
|
||||
console.log('nodes:', nodes)
|
||||
|
||||
const database = nodes.find((node) => node.props.name === name)
|
||||
return database
|
||||
}
|
||||
|
||||
addColumn = async (databaseId: string, fieldType: FieldType) => {
|
||||
const space = await this.getActiveSpace()
|
||||
const spaceId = space.id
|
||||
|
||||
@@ -19,6 +19,8 @@ export function onKeyDown(
|
||||
onKeyDown.onEnterEscapeFromEmptyList(editor, event) ||
|
||||
onKeyDown.onEnterSplitNonEmptyList(editor, event)
|
||||
)
|
||||
} catch (e) {
|
||||
console.log('onkeydown', e)
|
||||
} finally {
|
||||
// Slate does not always trigger normalization when one would expect it to.
|
||||
// So we want to force it after we perform lists operations, as it fixes
|
||||
|
||||
@@ -222,40 +222,15 @@ export const store = Object.assign(createStore(), {
|
||||
this.reloadNode(node)
|
||||
},
|
||||
|
||||
async createTag(text: string) {
|
||||
const space = this.getActiveSpace()
|
||||
async createDatabase(tagName: string) {
|
||||
const nodes = store.getNodes()
|
||||
|
||||
let tagRootNode = nodes.find((node) => node.type === NodeType.TAG_ROOT)!
|
||||
|
||||
let tagNode = nodes.find(
|
||||
(node) => node.type === NodeType.DATABASE && node.props.tag === text,
|
||||
let databaseNode = nodes.find(
|
||||
(node) => node.type === NodeType.DATABASE && node.props.name === tagName,
|
||||
)
|
||||
|
||||
if (!tagNode) {
|
||||
const newNode = await db.createNode({
|
||||
spaceId: space.id,
|
||||
parentId: tagRootNode.id,
|
||||
type: NodeType.DATABASE,
|
||||
props: {
|
||||
tag: text,
|
||||
// TODO:
|
||||
columns: [
|
||||
{
|
||||
title: text,
|
||||
type: 'TEXT',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
newNode.parentId
|
||||
|
||||
await db.updateNode(tagRootNode.id, {
|
||||
children: [...tagRootNode.children, newNode.id],
|
||||
})
|
||||
|
||||
await db.createDatabase(text)
|
||||
if (!databaseNode) {
|
||||
await db.createDatabase(tagName)
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@ export enum NodeType {
|
||||
TRASH = 'TRASH',
|
||||
DAILY_NOTE = 'DAILY_NOTE',
|
||||
|
||||
TAG_ROOT = 'TAG_ROOT',
|
||||
TAG = 'TAG',
|
||||
|
||||
// Database
|
||||
DATABASE_ROOT = 'DATABASE_ROOT',
|
||||
DATABASE = 'DATABASE',
|
||||
CELL = 'CELL',
|
||||
ROW = 'ROW',
|
||||
|
||||
Reference in New Issue
Block a user