Commit 89d940d9 authored by Steven's avatar Steven

feat: implement params field for embedded content node

parent bd1cf627
...@@ -66,7 +66,7 @@ func convertFromASTNode(rawNode ast.Node) *apiv2pb.Node { ...@@ -66,7 +66,7 @@ func convertFromASTNode(rawNode ast.Node) *apiv2pb.Node {
case *ast.Table: case *ast.Table:
node.Node = &apiv2pb.Node_TableNode{TableNode: convertTableFromASTNode(n)} node.Node = &apiv2pb.Node_TableNode{TableNode: convertTableFromASTNode(n)}
case *ast.EmbeddedContent: case *ast.EmbeddedContent:
node.Node = &apiv2pb.Node_EmbeddedContentNode{EmbeddedContentNode: &apiv2pb.EmbeddedContentNode{ResourceName: n.ResourceName}} node.Node = &apiv2pb.Node_EmbeddedContentNode{EmbeddedContentNode: &apiv2pb.EmbeddedContentNode{ResourceName: n.ResourceName, Params: n.Params}}
case *ast.Text: case *ast.Text:
node.Node = &apiv2pb.Node_TextNode{TextNode: &apiv2pb.TextNode{Content: n.Content}} node.Node = &apiv2pb.Node_TextNode{TextNode: &apiv2pb.TextNode{Content: n.Content}}
case *ast.Bold: case *ast.Bold:
...@@ -145,7 +145,7 @@ func convertToASTNode(node *apiv2pb.Node) ast.Node { ...@@ -145,7 +145,7 @@ func convertToASTNode(node *apiv2pb.Node) ast.Node {
case *apiv2pb.Node_TableNode: case *apiv2pb.Node_TableNode:
return convertTableToASTNode(node) return convertTableToASTNode(node)
case *apiv2pb.Node_EmbeddedContentNode: case *apiv2pb.Node_EmbeddedContentNode:
return &ast.EmbeddedContent{ResourceName: n.EmbeddedContentNode.ResourceName} return &ast.EmbeddedContent{ResourceName: n.EmbeddedContentNode.ResourceName, Params: n.EmbeddedContentNode.Params}
case *apiv2pb.Node_TextNode: case *apiv2pb.Node_TextNode:
return &ast.Text{Content: n.TextNode.Content} return &ast.Text{Content: n.TextNode.Content}
case *apiv2pb.Node_BoldNode: case *apiv2pb.Node_BoldNode:
......
...@@ -208,7 +208,7 @@ func (*Table) Type() NodeType { ...@@ -208,7 +208,7 @@ func (*Table) Type() NodeType {
} }
func (n *Table) Restore() string { func (n *Table) Restore() string {
var result string result := ""
for _, header := range n.Header { for _, header := range n.Header {
result += fmt.Sprintf("| %s ", header) result += fmt.Sprintf("| %s ", header)
} }
...@@ -233,6 +233,7 @@ type EmbeddedContent struct { ...@@ -233,6 +233,7 @@ type EmbeddedContent struct {
BaseBlock BaseBlock
ResourceName string ResourceName string
Params string
} }
func (*EmbeddedContent) Type() NodeType { func (*EmbeddedContent) Type() NodeType {
...@@ -240,5 +241,9 @@ func (*EmbeddedContent) Type() NodeType { ...@@ -240,5 +241,9 @@ func (*EmbeddedContent) Type() NodeType {
} }
func (n *EmbeddedContent) Restore() string { func (n *EmbeddedContent) Restore() string {
return fmt.Sprintf("![[%s]]", n.ResourceName) result := fmt.Sprintf("![[%s]]", n.ResourceName)
if n.Params != "" {
result += fmt.Sprintf("?%s", n.Params)
}
return result
} }
...@@ -45,7 +45,18 @@ func (p *EmbeddedContentParser) Parse(tokens []*tokenizer.Token) (ast.Node, erro ...@@ -45,7 +45,18 @@ func (p *EmbeddedContentParser) Parse(tokens []*tokenizer.Token) (ast.Node, erro
return nil, errors.New("not matched") return nil, errors.New("not matched")
} }
contentTokens := tokens[3 : size-2]
resourceName, params := "", ""
paramsIndex, ok := tokenizer.Find(contentTokens, tokenizer.QuestionMark)
if ok && paramsIndex > 0 {
resourceName = tokenizer.Stringify(contentTokens[:paramsIndex])
params = tokenizer.Stringify(contentTokens[paramsIndex+1:])
} else {
resourceName = tokenizer.Stringify(contentTokens)
}
return &ast.EmbeddedContent{ return &ast.EmbeddedContent{
ResourceName: tokenizer.Stringify(tokens[3 : size-2]), ResourceName: resourceName,
Params: params,
}, nil }, nil
} }
...@@ -41,6 +41,13 @@ func TestEmbeddedContentParser(t *testing.T) { ...@@ -41,6 +41,13 @@ func TestEmbeddedContentParser(t *testing.T) {
ResourceName: "resources/101", ResourceName: "resources/101",
}, },
}, },
{
text: "![[resources/101?align=center]]\n123",
embeddedContent: &ast.EmbeddedContent{
ResourceName: "resources/101",
Params: "align=center",
},
},
} }
for _, test := range tests { for _, test := range tests {
......
...@@ -12,6 +12,7 @@ const ( ...@@ -12,6 +12,7 @@ const (
LeftParenthesis TokenType = "(" LeftParenthesis TokenType = "("
RightParenthesis TokenType = ")" RightParenthesis TokenType = ")"
ExclamationMark TokenType = "!" ExclamationMark TokenType = "!"
QuestionMark TokenType = "?"
Tilde TokenType = "~" Tilde TokenType = "~"
Hyphen TokenType = "-" Hyphen TokenType = "-"
PlusSign TokenType = "+" PlusSign TokenType = "+"
...@@ -67,6 +68,8 @@ func Tokenize(text string) []*Token { ...@@ -67,6 +68,8 @@ func Tokenize(text string) []*Token {
tokens = append(tokens, NewToken(RightParenthesis, ")")) tokens = append(tokens, NewToken(RightParenthesis, ")"))
case '!': case '!':
tokens = append(tokens, NewToken(ExclamationMark, "!")) tokens = append(tokens, NewToken(ExclamationMark, "!"))
case '?':
tokens = append(tokens, NewToken(QuestionMark, "?"))
case '~': case '~':
tokens = append(tokens, NewToken(Tilde, "~")) tokens = append(tokens, NewToken(Tilde, "~"))
case '-': case '-':
...@@ -149,3 +152,12 @@ func Split(tokens []*Token, delimiter TokenType) [][]*Token { ...@@ -149,3 +152,12 @@ func Split(tokens []*Token, delimiter TokenType) [][]*Token {
result = append(result, current) result = append(result, current)
return result return result
} }
func Find(tokens []*Token, delimiter TokenType) (int, bool) {
for index, token := range tokens {
if token.Type == delimiter {
return index, true
}
}
return 0, false
}
...@@ -146,6 +146,7 @@ message TableNode { ...@@ -146,6 +146,7 @@ message TableNode {
message EmbeddedContentNode { message EmbeddedContentNode {
string resource_name = 1; string resource_name = 1;
string params = 2;
} }
message TextNode { message TextNode {
......
...@@ -1070,6 +1070,7 @@ ...@@ -1070,6 +1070,7 @@
| Field | Type | Label | Description | | Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- | | ----- | ---- | ----- | ----------- |
| resource_name | [string](#string) | | | | resource_name | [string](#string) | | |
| params | [string](#string) | | |
......
This diff is collapsed.
...@@ -6,6 +6,7 @@ import Error from "./Error"; ...@@ -6,6 +6,7 @@ import Error from "./Error";
interface Props { interface Props {
memoId: number; memoId: number;
params: string;
} }
const EmbeddedMemo = ({ memoId }: Props) => { const EmbeddedMemo = ({ memoId }: Props) => {
......
import classNames from "classnames";
import { useEffect } from "react"; import { useEffect } from "react";
import MemoResourceListView from "@/components/MemoResourceListView"; import MemoResourceListView from "@/components/MemoResourceListView";
import { useResourceStore } from "@/store/v1"; import { useResourceStore } from "@/store/v1";
interface Props { interface Props {
resourceId: number; resourceId: number;
params: string;
} }
const EmbeddedResource = ({ resourceId }: Props) => { const getAdditionalClassNameWithParams = (params: URLSearchParams) => {
const additionalClassNames = [];
if (params.has("align")) {
const align = params.get("align");
if (align === "center") {
additionalClassNames.push("mx-auto");
}
}
if (params.has("size")) {
const size = params.get("size");
if (size === "lg") {
additionalClassNames.push("w-full");
} else if (size === "md") {
additionalClassNames.push("w-2/3");
} else if (size === "sm") {
additionalClassNames.push("w-1/3");
}
}
if (params.has("width")) {
const width = params.get("width");
additionalClassNames.push(`w-[${width}]`);
}
return additionalClassNames.join(" ");
};
const EmbeddedResource = ({ resourceId, params: paramsStr }: Props) => {
const resourceStore = useResourceStore(); const resourceStore = useResourceStore();
const resource = resourceStore.getResourceById(resourceId); const resource = resourceStore.getResourceById(resourceId);
const params = new URLSearchParams(paramsStr);
useEffect(() => { useEffect(() => {
resourceStore.getOrFetchResourceById(resourceId); resourceStore.getOrFetchResourceById(resourceId);
...@@ -18,7 +46,11 @@ const EmbeddedResource = ({ resourceId }: Props) => { ...@@ -18,7 +46,11 @@ const EmbeddedResource = ({ resourceId }: Props) => {
return null; return null;
} }
return <MemoResourceListView resources={[resource]} />; return (
<div className={classNames("max-w-full", getAdditionalClassNameWithParams(params))}>
<MemoResourceListView resources={[resource]} />
</div>
);
}; };
export default EmbeddedResource; export default EmbeddedResource;
...@@ -4,6 +4,7 @@ import Error from "./Error"; ...@@ -4,6 +4,7 @@ import Error from "./Error";
interface Props { interface Props {
resourceName: string; resourceName: string;
params: string;
} }
const extractResourceTypeAndId = (resourceName: string) => { const extractResourceTypeAndId = (resourceName: string) => {
...@@ -11,12 +12,12 @@ const extractResourceTypeAndId = (resourceName: string) => { ...@@ -11,12 +12,12 @@ const extractResourceTypeAndId = (resourceName: string) => {
return { resourceType, resourceId }; return { resourceType, resourceId };
}; };
const EmbeddedContent = ({ resourceName }: Props) => { const EmbeddedContent = ({ resourceName, params }: Props) => {
const { resourceType, resourceId } = extractResourceTypeAndId(resourceName); const { resourceType, resourceId } = extractResourceTypeAndId(resourceName);
if (resourceType === "memos") { if (resourceType === "memos") {
return <EmbeddedMemo memoId={Number(resourceId)} />; return <EmbeddedMemo memoId={Number(resourceId)} params={params} />;
} else if (resourceType === "resources") { } else if (resourceType === "resources") {
return <EmbeddedResource resourceId={Number(resourceId)} />; return <EmbeddedResource resourceId={Number(resourceId)} params={params} />;
} }
return <Error message={`Unknown resource: ${resourceName}`} />; return <Error message={`Unknown resource: ${resourceName}`} />;
}; };
......
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