Commit ca213437 authored by Steven's avatar Steven

feat: implement nesting lists

parent 7a4d54bb
This diff is collapsed.
......@@ -23,7 +23,7 @@ require (
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.9.0
github.com/usememos/gomark v0.0.0-20240920000613-9640de1955cd
github.com/usememos/gomark v0.0.0-20240921115339-c9d3461efcd8
golang.org/x/crypto v0.27.0
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0
golang.org/x/mod v0.21.0
......
......@@ -438,8 +438,8 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/usememos/gomark v0.0.0-20240920000613-9640de1955cd h1:tqBJ00an5/MWAu1pIFkyE9TCSIe1JYAhShJlVeveYgs=
github.com/usememos/gomark v0.0.0-20240920000613-9640de1955cd/go.mod h1:7CZRoYFQyyljzplOTeyODFR26O+wr0BbnpTWVLGfKJA=
github.com/usememos/gomark v0.0.0-20240921115339-c9d3461efcd8 h1:kutNP+XD8oiBzoJJER35fbVaphCeRhU1dKDjZ5eKT0M=
github.com/usememos/gomark v0.0.0-20240921115339-c9d3461efcd8/go.mod h1:7CZRoYFQyyljzplOTeyODFR26O+wr0BbnpTWVLGfKJA=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
......
......@@ -173,7 +173,15 @@ message BlockquoteNode {
}
message ListNode {
repeated Node children = 1;
enum Kind {
KIND_UNSPECIFIED = 0;
ORDERED = 1;
UNORDERED = 2;
DESCRIPTION = 3;
}
Kind kind = 1;
int32 indent = 2;
repeated Node children = 3;
}
message OrderedListItemNode {
......
This diff is collapsed.
......@@ -77,7 +77,7 @@ func convertFromASTNode(rawNode ast.Node) *v1pb.Node {
node.Node = &v1pb.Node_BlockquoteNode{BlockquoteNode: &v1pb.BlockquoteNode{Children: children}}
case *ast.List:
children := convertFromASTNodes(n.Children)
node.Node = &v1pb.Node_ListNode{ListNode: &v1pb.ListNode{Children: children}}
node.Node = &v1pb.Node_ListNode{ListNode: &v1pb.ListNode{Kind: convertListKindFromASTNode(n.Kind), Indent: int32(n.Indent), Children: children}}
case *ast.OrderedListItem:
children := convertFromASTNodes(n.Children)
node.Node = &v1pb.Node_OrderedListItemNode{OrderedListItemNode: &v1pb.OrderedListItemNode{Number: n.Number, Indent: int32(n.Indent), Children: children}}
......@@ -156,6 +156,19 @@ func convertTableFromASTNode(node *ast.Table) *v1pb.TableNode {
return table
}
func convertListKindFromASTNode(node ast.ListKind) v1pb.ListNode_Kind {
switch node {
case ast.OrderedList:
return v1pb.ListNode_ORDERED
case ast.UnorderedList:
return v1pb.ListNode_UNORDERED
case ast.DescrpitionList:
return v1pb.ListNode_DESCRIPTION
default:
return v1pb.ListNode_KIND_UNSPECIFIED
}
}
func convertToASTNode(node *v1pb.Node) ast.Node {
switch n := node.Node.(type) {
case *v1pb.Node_LineBreakNode:
......@@ -175,7 +188,7 @@ func convertToASTNode(node *v1pb.Node) ast.Node {
return &ast.Blockquote{Children: children}
case *v1pb.Node_ListNode:
children := convertToASTNodes(n.ListNode.Children)
return &ast.List{Children: children}
return &ast.List{Kind: convertListKindToASTNode(n.ListNode.Kind), Indent: int(n.ListNode.Indent), Children: children}
case *v1pb.Node_OrderedListItemNode:
children := convertToASTNodes(n.OrderedListItemNode.Children)
return &ast.OrderedListItem{Number: n.OrderedListItemNode.Number, Indent: int(n.OrderedListItemNode.Indent), Children: children}
......@@ -252,3 +265,17 @@ func convertTableToASTNode(node *v1pb.TableNode) *ast.Table {
}
return table
}
func convertListKindToASTNode(kind v1pb.ListNode_Kind) ast.ListKind {
switch kind {
case v1pb.ListNode_ORDERED:
return ast.OrderedList
case v1pb.ListNode_UNORDERED:
return ast.UnorderedList
case v1pb.ListNode_DESCRIPTION:
return ast.DescrpitionList
default:
// Default to description list.
return ast.DescrpitionList
}
}
import { Node, NodeType } from "@/types/proto/api/v1/markdown_service";
import clsx from "clsx";
import React from "react";
import { ListNode_Kind, Node, NodeType } from "@/types/proto/api/v1/markdown_service";
import Renderer from "./Renderer";
interface Props {
index: string;
kind: ListNode_Kind;
indent: number;
children: Node[];
}
const List: React.FC<Props> = ({ children }: Props) => {
const List: React.FC<Props> = ({ kind, indent, children }: Props) => {
let prevNode: Node | null = null;
let skipNextLineBreakFlag = false;
return (
<dl>
{children.map((child, index) => {
const getListContainer = (kind: ListNode_Kind) => {
switch (kind) {
case ListNode_Kind.ORDERED:
return "ol";
case ListNode_Kind.UNORDERED:
return "ul";
case ListNode_Kind.DESCRIPTION:
return "dl";
default:
return "div";
}
};
return React.createElement(
getListContainer(kind),
{
className: clsx(
`list-inside ${kind === ListNode_Kind.ORDERED ? "list-decimal" : kind === ListNode_Kind.UNORDERED ? "list-disc" : "list-none"}`,
indent > 0 ? `pl-${2 * indent}` : "",
),
},
children.map((child, index) => {
if (prevNode?.type !== NodeType.LINE_BREAK && child.type === NodeType.LINE_BREAK && skipNextLineBreakFlag) {
skipNextLineBreakFlag = false;
return null;
......@@ -21,8 +44,7 @@ const List: React.FC<Props> = ({ children }: Props) => {
prevNode = child;
skipNextLineBreakFlag = true;
return <Renderer key={`${child.type}-${index}`} index={String(index)} node={child} />;
})}
</dl>
}),
);
};
......
import { repeat } from "lodash-es";
import { Node } from "@/types/proto/api/v1/markdown_service";
import Renderer from "./Renderer";
import { BaseProps } from "./types";
......@@ -9,24 +8,12 @@ interface Props extends BaseProps {
children: Node[];
}
const OrderedListItem: React.FC<Props> = ({ number, indent, children }: Props) => {
const OrderedListItem: React.FC<Props> = ({ children }: Props) => {
return (
<li className="w-full flex flex-row">
{indent > 0 && (
<div className="block font-mono shrink-0">
<span>{repeat(" ", indent)}</span>
</div>
)}
<div className="w-auto grid grid-cols-[24px_1fr] gap-1">
<div className="w-7 h-6 flex justify-center items-center">
<span className="opacity-80">{number}.</span>
</div>
<div>
<li>
{children.map((child, index) => (
<Renderer key={`${child.type}-${index}`} index={String(index)} node={child} />
))}
</div>
</div>
</li>
);
};
......
import { Checkbox } from "@mui/joy";
import clsx from "clsx";
import { repeat } from "lodash-es";
import { useContext, useState } from "react";
import { markdownServiceClient } from "@/grpcweb";
import { useMemoStore } from "@/store/v1";
......@@ -17,7 +16,7 @@ interface Props {
children: Node[];
}
const TaskListItem: React.FC<Props> = ({ node, indent, complete, children }: Props) => {
const TaskListItem: React.FC<Props> = ({ node, complete, children }: Props) => {
const context = useContext(RendererContext);
const memoStore = useMemoStore();
const [checked] = useState(complete);
......@@ -39,22 +38,15 @@ const TaskListItem: React.FC<Props> = ({ node, indent, complete, children }: Pro
};
return (
<li className="w-full flex flex-row">
{indent > 0 && (
<div className="block font-mono shrink-0">
<span>{repeat(" ", indent)}</span>
</div>
)}
<div className="w-auto grid grid-cols-[24px_1fr] gap-1">
<div className="w-7 h-6 flex justify-center items-center">
<li className={clsx("w-full grid grid-cols-[24px_1fr]")}>
<span className="w-6 h-6 flex justify-start items-center">
<Checkbox size="sm" checked={checked} disabled={context.readonly} onChange={(e) => handleCheckboxChange(e.target.checked)} />
</div>
<div className={clsx(complete && "line-through opacity-80")}>
</span>
<p className={clsx(complete && "line-through opacity-80")}>
{children.map((child, index) => (
<Renderer key={`${child.type}-${index}`} index={String(index)} node={child} />
))}
</div>
</div>
</p>
</li>
);
};
......
import { repeat } from "lodash-es";
import { Node } from "@/types/proto/api/v1/markdown_service";
import Renderer from "./Renderer";
......@@ -8,24 +7,12 @@ interface Props {
children: Node[];
}
const UnorderedListItem: React.FC<Props> = ({ indent, children }: Props) => {
const UnorderedListItem: React.FC<Props> = ({ children }: Props) => {
return (
<li className="w-full flex flex-row">
{indent > 0 && (
<div className="block font-mono shrink-0">
<span>{repeat(" ", indent)}</span>
</div>
)}
<div className="w-auto grid grid-cols-[24px_1fr] gap-1">
<div className="w-7 h-6 flex justify-center items-center">
<span className="opacity-80"></span>
</div>
<div>
<li>
{children.map((child, index) => (
<Renderer key={`${child.type}-${index}`} index={String(index)} node={child} />
))}
</div>
</div>
</li>
);
};
......
......@@ -443,7 +443,7 @@ const MemoEditor = (props: Props) => {
<AddMemoRelationPopover editorRef={editorRef} />
</div>
</div>
<Divider className="!mt-2" />
<Divider className="!mt-2 opacity-40" />
<div className="w-full flex flex-row justify-between items-center py-3 dark:border-t-zinc-500">
<div className="relative flex flex-row justify-start items-center" onFocus={(e) => e.stopPropagation()}>
<Select
......
......@@ -23,8 +23,8 @@ const UserBanner = (props: Props) => {
const workspaceSettingStore = useWorkspaceSettingStore();
const workspaceGeneralSetting =
workspaceSettingStore.getWorkspaceSettingByKey(WorkspaceSettingKey.GENERAL).generalSetting || WorkspaceGeneralSetting.fromPartial({});
const title = user ? user.nickname || user.username : workspaceGeneralSetting.customProfile?.title;
const avatarUrl = user ? user.avatarUrl : workspaceGeneralSetting.customProfile?.logoUrl;
const title = (user ? user.nickname || user.username : workspaceGeneralSetting.customProfile?.title) || "Memos";
const avatarUrl = (user ? user.avatarUrl : workspaceGeneralSetting.customProfile?.logoUrl) || "/logo.webp";
const handleSignOut = async () => {
await authServiceClient.signOut({});
......
......@@ -81,20 +81,6 @@ module.exports = {
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
keyframes: {
"accordion-down": {
from: { height: "0" },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: "0" },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
},
},
plugins: [require("tailwindcss-animate")],
......
{
"rewrites": [
{
"source": "/(.*)",
"destination": "/index.html"
}
]
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment