Skip to content

Commit

Permalink
Fix several problems; add antd Tour
Browse files Browse the repository at this point in the history
  • Loading branch information
tansongchen committed Jan 17, 2024
1 parent 7190357 commit 760a5dc
Show file tree
Hide file tree
Showing 17 changed files with 254 additions and 112 deletions.
4 changes: 2 additions & 2 deletions spec/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
"extends": "../tsconfig.json",
"compilerOptions": {
"noUncheckedIndexedAccess": false,
"types": []
}
"types": [],
},
}
4 changes: 2 additions & 2 deletions src/atoms/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export const userWordFrequencyAtom = atomWithStorage<Frequency | null>(
null,
);

export const userWordAtom = atomWithStorage<Frequency | null>(
"user_word",
export const userWordAtom = atomWithStorage<string[] | null>(
"user_word_list",
null,
);

Expand Down
9 changes: 8 additions & 1 deletion src/atoms/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
userCharacterFrequencyAtom,
userKeyDistributionAtom,
userPairEquivalenceAtom,
userWordAtom,
userWordFrequencyAtom,
} from "./assets";

Expand Down Expand Up @@ -56,6 +57,11 @@ export const wordFrequencyAtom = atom<Frequency>({});
export const keyDistributionAtom = atom<Frequency>({});
export const pairEquivalenceAtom = atom<Equivalence>({});

export const wordAtom = atom((get) => {
const wordFrequency = get(wordFrequencyAtom);
return Object.keys(wordFrequency);
});

export interface Assets {
character_frequency: Frequency;
word_frequency: Frequency;
Expand All @@ -81,6 +87,7 @@ export const assetsAtom = atom((get) => {
});

export const wordsAtom = atom((get) => {
const userWord = get(userWordAtom);
const { word_frequency } = get(assetsAtom);
return Object.keys(word_frequency);
return userWord ?? Object.keys(word_frequency);
});
6 changes: 3 additions & 3 deletions src/atoms/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,14 @@ export const repertoireAtom = atom((get) => {
});

export const glyphAtom = atom((get) => {
const determinedRepertoire = get(repertoireAtom);
const repertoire = get(repertoireAtom);
const result = new Map<string, SVGGlyph>();
for (const [char, { glyph }] of Object.entries(determinedRepertoire)) {
for (const [char, { glyph }] of Object.entries(repertoire)) {
if (glyph === undefined) continue;
if (glyph.type === "basic_component") {
result.set(char, glyph.strokes);
} else {
const svgglyph = recursiveRenderCompound(glyph, determinedRepertoire);
const svgglyph = recursiveRenderCompound(glyph, repertoire);
if (svgglyph instanceof Error) continue;
result.set(char, svgglyph);
}
Expand Down
33 changes: 20 additions & 13 deletions src/components/Action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ import {
Popover,
Space,
} from "antd";
import { createContext, useContext, useState } from "react";
import {
ForwardedRef,
createContext,
forwardRef,
useContext,
useState,
} from "react";
import {
remoteCreate,
remoteCreateWithoutUnicode,
Expand All @@ -18,7 +24,6 @@ import {
} from "~/lib/api";
import {
DeleteButton,
PlusButton,
Select,
errorFeedback,
verifyNewName,
Expand All @@ -44,12 +49,7 @@ import {
primitiveRepertoireAtom,
customGlyphAtom,
} from "~/atoms";
import {
PrimitveCharacter,
DerivedComponent,
Compound,
Component,
} from "~/lib/data";
import { PrimitveCharacter, Compound, Component } from "~/lib/data";
import ComponentForm from "./ComponentForm";
import CompoundForm from "./CompoundForm";
import { MenuProps } from "antd/lib";
Expand All @@ -62,10 +62,17 @@ interface CreateProps {

export const RemoteContext = createContext(true);

export const Create = ({ onCreate }: { onCreate: (s: string) => void }) => (
<Popover content={<CreatePopoverContent onCreate={onCreate} />}>
<Button type="primary">新建</Button>
</Popover>
export const Create = forwardRef(
(
{ onCreate }: { onCreate: (s: string) => void },
ref: ForwardedRef<HTMLElement>,
) => (
<Popover content={<CreatePopoverContent onCreate={onCreate} />}>
<Button type="primary" ref={ref}>
新建
</Button>
</Popover>
),
);

function CreatePopoverContent({ onCreate }: { onCreate: (s: string) => void }) {
Expand Down Expand Up @@ -136,7 +143,7 @@ function CreatePopoverContent({ onCreate }: { onCreate: (s: string) => void }) {
}}
>
<Form.Item<CreateProps>
label="名称"
label="字或别名"
name="charOrName"
rules={[
{
Expand Down
127 changes: 96 additions & 31 deletions src/components/CharacterTable.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import React, { useContext, useState } from "react";
import React, { useContext, useRef, useState } from "react";
import { isPUA, unicodeBlock } from "~/lib/utils";
import { Checkbox, Flex, Layout, Space } from "antd";
import {
Checkbox,
Flex,
FloatButton,
Layout,
Space,
Tour,
Typography,
} from "antd";
import type { ColumnType, ColumnsType } from "antd/es/table";
import Table from "antd/es/table";
import {
Expand Down Expand Up @@ -35,6 +43,9 @@ import CharacterQuery, {
makeCharacterFilter,
} from "./CharacterQuery";
import TagPicker from "./TagPicker";
import { findGlyphIndex } from "~/lib/repertoire";
import { TourProps } from "antd/lib";
import { QuestionCircleOutlined } from "@ant-design/icons";

type Column = ColumnType<PrimitveCharacter>;

Expand All @@ -44,6 +55,7 @@ const CharacterTable = () => {
const addUser = useAddAtom(userRepertoireAtom);
const userTags = useAtomValue(userTagsAtom);
const customGlyph = useAtomValue(customGlyphAtom);
const addCustomGlyph = useAddAtom(customGlyphAtom);
const customReadings = useAtomValue(customReadingsAtom);
const sequenceMap = useAtomValue(sequenceAtom);
const [page, setPage] = useState(1);
Expand All @@ -53,12 +65,12 @@ const CharacterTable = () => {
const remote = useContext(RemoteContext);
const add = useAddAtom(primitiveRepertoireAtom);
const filter = makeCharacterFilter(filterProps, allRepertoire, sequenceMap);
const isUserPUA = (a: string) =>
-Number(a.codePointAt(0)! >= 0xf000 && a.codePointAt(0)! <= 0x10000);
const isUserCharacter = (a: string) =>
-Number(userRepertoire[a] !== undefined);

const dataSource = Object.entries(allRepertoire)
.sort(([a], [b]) => getLength(a) - getLength(b))
.sort(([a], [b]) => isUserPUA(a) - isUserPUA(b))
.sort(([a], [b]) => isUserCharacter(a) - isUserCharacter(b))
.filter(([x]) => filter(x))
.map(([, glyph]) => glyph);

Expand Down Expand Up @@ -152,11 +164,12 @@ const CharacterTable = () => {
}
return true;
};
const selectedIndex = findGlyphIndex(glyphs, userTags);
return (
<Flex gap="small">
{glyphs.map((x, i) => {
const lens = O.compose("glyphs", O.at(i));
const primary = userTags.some((tag) => x.tags?.includes(tag));
const primary = i === selectedIndex;
return (
<Space key={i}>
{x.type === "compound" ? (
Expand All @@ -175,6 +188,7 @@ const CharacterTable = () => {
return inlineUpdate(newGlyphs);
}}
primary={primary}
readonly={!remote && userRepertoire[char] === undefined}
/>
) : (
<ComponentForm
Expand All @@ -191,9 +205,10 @@ const CharacterTable = () => {
return inlineUpdate(newGlyphs);
}}
primary={primary}
readonly={!remote && userRepertoire[char] === undefined}
/>
)}
{remote ? (
{remote || userRepertoire[char] !== undefined ? (
<DeleteButton
onClick={() => inlineUpdate(O.remove(lens, character))}
/>
Expand All @@ -215,7 +230,7 @@ const CharacterTable = () => {
const customGlyphColumn: Column = {
title: "自定义字形",
render: (_, character) => {
const { glyphs, unicode } = character;
const { unicode } = character;
const char = String.fromCodePoint(unicode);
const customized = customGlyph[char];
if (customized === undefined) return null;
Expand All @@ -228,14 +243,22 @@ const CharacterTable = () => {
customized.operandList.map(display).join(" ")
}
initialValues={customized}
onFinish={async (values) => true}
onFinish={async (values) => {
addCustomGlyph(char, values);
return true;
}}
primary
/>
) : (
<ComponentForm
title={`部件`}
initialValues={customized}
current={String.fromCodePoint(unicode)}
onFinish={async (values) => true}
onFinish={async (values) => {
addCustomGlyph(char, values);
return true;
}}
primary
/>
)}
</Flex>
Expand Down Expand Up @@ -287,6 +310,33 @@ const CharacterTable = () => {
},
};

const ref1 = useRef(null);
const ref2 = useRef(null);
const ref3 = useRef(null);

const [open, setOpen] = useState<boolean>(false);

const steps: TourProps["steps"] = [
{
title: "自定义",
description:
"这里存放了汉字编码所需要的字音和字形数据。一个字可能会有零个、一个或多个字音和字形表示,默认情况下所有的字音表示都会用于生成编码,但是字形表示中只有第一个会参与编码。对于字形,如果系统中的第一个不是您想要的,您可以通过点击「自定义」来选择系统中的其他字形用于编码,或者自己创建一个部件或者复合体表示。",
target: () => ref1.current,
},
{
title: "批量自定义",
description:
"除此之外,您还可以在下方的「通过标签来批量选择字形」中选择一系列标签,被标签选中的系统字形会优先参与编码(例如,若您选择标签「行框」,则所有如街、衔、衡等的汉字都会选择[⿻ 行 ?]的分部方式,而不是原本排在第一位的左中右分部方式)。被选中的字形会以框选的方式突出显示。",
target: () => ref2.current,
},
{
title: "新建",
description:
"最后,您还可以通过点击「新建」来添加系统中没有的字或者您需要的特殊字根。新加的条目位于表格的最上方。若这个字或字根不属于 CJK 基本集或者 CJK 扩展 A,则您需要输入它的别名,系统会给它安排一个 0xF000 开头的 PUA 码位存放。",
target: () => ref3.current,
},
];

const adminColumns = [
unicodeColumn,
readings,
Expand All @@ -308,31 +358,46 @@ const CharacterTable = () => {
return (
<Flex
component={Layout.Content}
style={{ padding: "32px", overflowY: "scroll" }}
style={{ overflowY: "scroll" }}
vertical
gap="large"
align="center"
>
<CharacterQuery setFilter={setFilterProps} />
<TagPicker />
<Create onCreate={(char) => {}} />
<Table<PrimitveCharacter>
dataSource={dataSource}
columns={columns}
size="small"
rowKey="unicode"
pagination={{
pageSize: 50,
current: page,
}}
onChange={(pagination) => {
const current = pagination.current;
current && setPage(current);
}}
style={{
maxWidth: "1920px",
}}
/>
<Flex
gap="large"
style={{ alignSelf: "stretch", paddingInline: "32px" }}
ref={ref2}
>
<TagPicker />
<div style={{ flex: 1 }} />
<Create onCreate={(char) => {}} ref={ref3} />
</Flex>
<div ref={ref1}>
<Table<PrimitveCharacter>
dataSource={dataSource}
columns={columns}
size="small"
rowKey="unicode"
pagination={{
pageSize: 50,
current: page,
}}
onChange={(pagination) => {
const current = pagination.current;
current && setPage(current);
}}
style={{
maxWidth: "1920px",
}}
/>
<Tour open={open} onClose={() => setOpen(false)} steps={steps} />
<FloatButton
icon={<QuestionCircleOutlined />}
type="primary"
style={{ right: 64 }}
onClick={() => setOpen(true)}
/>
</div>
</Flex>
);
};
Expand Down
Loading

0 comments on commit 760a5dc

Please sign in to comment.