function isRadicalStandIn
isRadicalStandIn(key: string): boolean
True when the key is a stand-in, so that the form, not the key, is the thing to draw and to name.
@johnmorrisdotca/bushu 1.0.0 · 7 entry points · 38 exports
@johnmorrisdotca/bushuisRadicalStandIn kanjiForRadicals orderChosen Radical RADICAL_FORMS radicalForm RadicalGroup radicalGroups radicalsInKanji radicalStrokes RADKFILE_STROKE_CORRECTIONS usableRadicals VERSION withCorrectedStrokes
isRadicalStandIn(key: string): boolean
True when the key is a stand-in, so that the form, not the key, is the thing to draw and to name.
kanjiForRadicals(radicals: readonly Radical[], chosen: readonly string[]): string[]
The kanji that hold every one of the chosen radicals, in the order of the first radical's list (the dictionary's own).
Nothing chosen means nothing to show: the whole dictionary is not an answer to a question nobody has asked yet. A radical that is not in the list cannot be satisfied, so the answer is nothing.
orderChosen(radicals: readonly Radical[], chosen: readonly string[]): string[]
The chosen radicals in the grid's own order, not the order they were clicked, so the choice reads as a row of the grid. A radical the grid does not have is dropped.
type Radical = { /** Its key. A radical with no character of its own is keyed by a kanji that holds its shape: 汁 for 氵 (see `radicalForm`). */ readonly radical: string; /** Its stroke count as RADKFILE writes it (see `withCorrectedStrokes` for the two it gets wrong). */ readonly strokes: number; /** Every kanji that holds it, as one string. */ readonly kanji: string; };
A radical, or a common element of kanji, as RADKFILE lists it.
RADICAL_FORMS: Readonly<Record<string, string>>
WHAT A RADKFILE RADICAL LOOKS LIKE, AS OPPOSED TO WHAT IT IS KEYED BY.
RADKFILE predates Unicode having a character for every radical shape, so for the shapes with no character of their own it writes a whole kanji that contains the shape: 汁 for 氵, 扎 for 扌, 艾 for 艹, 込 for 辶. The stroke count beside each is the radical's, not the stand-in's (汁 sits under three strokes because 氵 is three), and every kanji listed under it is a kanji written with the shape, not with the stand-in. Drawn as written, a picker files a five-stroke kanji under three strokes and names it "soup".
So the key stays the key, since it is what the index and everything that looks a radical up speak, and anything that DRAWS a radical asks radicalForm for its shape first. This table is the package's own. Each shape is the CJK Unified Ideograph that is that shape, which nearly every font draws (EDRDG's own list of the same characters, in the CJK Radicals Supplement and Kangxi blocks, is in RADKFILE.unicode from @johnmorrisdotca/bushu/radkfile, and a test holds the two to the same set of keys).
Both 邦 and 阡 come out as 阝, which is right: it is the same shape on the right of 部 and on the left of 陸, and RADKFILE keeps two keys because the two kanji lists differ. The name tells them apart (おおざと and こざとへん).
radicalForm(key: string): string
The shape to draw for a RADKFILE key: the stand-in's own shape (汁 gives 氵), or the key itself where it already is the shape.
type RadicalGroup = { strokes: number; radicals: string[] };
The radicals that share a stroke count, as the grid of a picker draws them.
radicalGroups(radicals: readonly Radical[]): RadicalGroup[]
The grid: the radicals in stroke-count order, grouped by the count, in the order they were given within a count.
radicalsInKanji(radicals: readonly Radical[], kanji: string): Radical[]
The radicals a character is written with, fewest strokes first.
RADKFILE is stored radical by radical, each with the kanji that hold it, because that is the direction a search runs. A kanji page asks the opposite question, and a membership test per radical answers it, which is nothing next to keeping a second copy of the index the other way round. Simplest parts first, so the list reads the way the character is built up. Code point order breaks a tie, never a locale's collation, so the same character lists its parts in the same order on every machine.
radicalStrokes(key: string, radkfileStrokes: number): number
The stroke count to file a RADKFILE radical under: its own, except for the two it miscounts.
RADKFILE_STROKE_CORRECTIONS: Readonly<Record<string, number>>
THE TWO STROKE COUNTS RADKFILE GETS WRONG.
Measured across all 253 against KANJIDIC2 and Kanji alive: 乞 is three strokes and RADKFILE files it under two; 舛 is six and it files it under seven. The other places the dictionary disagrees (辶, 艹 and 耂) are Kangxi counts (4, 6, 6), and RADKFILE's 3, 3 and 4 are what a Japanese school counts, so they stand.
Kept here, applied on reading (radicalStrokes, withCorrectedStrokes) and never edited into the data, so a rebuild from RADKFILE cannot bring the two back.
usableRadicals(radicals: readonly Radical[], chosen: readonly string[]): Set<string>
The radicals that can still narrow what is left: every one that appears in at least one of the kanji that match now.
A radical in none of the remaining kanji is a dead end, which a picker dims rather than let you click your way to an empty list. The chosen ones stay in the set: taking one back must always be possible. With nothing chosen, every radical can.
VERSION: "1.0.0"
The package's version.
withCorrectedStrokes(radicals: readonly Radical[]): Radical[]
The radicals with the two miscounts put right, as new entries in the same order. Hand it to everything that groups by strokes.
@johnmorrisdotca/bushu/radkfileRADKFILE: { sources: Record<"radkfile" | "kradfile", { name: string; url: string; fileDate: string | null; sha256: string; retrieved: string; }>; radicals: readonly Radical[]; unicode: Readonly<Record<string, string>>; }
THE RADICALS AND THEIR KANJI, from RADKFILE. Written by scripts/build-data.mjs, never edited by hand; the monthly refresh (.github/workflows/radkfile-refresh.yml) runs the script again.
RADKFILE and KRADFILE are the property of the Electronic Dictionary Research and Development Group (EDRDG), used under the Creative Commons Attribution-ShareAlike 4.0 licence and the Group's conditions: https://www.edrdg.org/edrdg/licence.html. This data is derived from them and is under the same licence (see NOTICE.md).
253 radicals, in the file's own order (fewest strokes first), each with its stroke count as RADKFILE writes it and every kanji that holds it; 6355 kanji in all (the JIS X 0208 set). A radical with no character of its own is keyed by a kanji that holds the shape (汁 for 氵): radicalForm says the shape. KRADFILE, which lists the same thing kanji by kanji, was checked to agree with RADKFILE on every one of the 6355 kanji before this was written. unicode is the character EDRDG means by a stand-in, from KRADFILE's header.
@johnmorrisdotca/bushu/namesRADICAL_NAMES radicalName radicalNameCount radicalNameEntry RadicalNameEntry radicalNameForShape
RADICAL_NAMES: { source: { name: string; publisher: string; url: string; commit: string; licence: string; files: Readonly<Record<string, string>>; retrieved: string; }; names: Readonly<Record<string, { name: string; romaji: string; meaning: string; position: string | null; kangxi: number | null; }>>; }
THE JAPANESE SCHOOL NAMES OF THE RADICALS, from Kanji alive. Written by scripts/build-names.ts (pnpm data:names), never edited by hand.
Kanji alive, by Harumi Hibino Lory and Arno Bosse (University of Chicago), is used under the Creative Commons Attribution 4.0 licence (https://creativecommons.org/licenses/by/4.0/); its radicals table is read from one commit of https://github.com/kanjialive/kanji-data-media. These names are derived from it and are under the same licence (see NOTICE.md).
227 of RADKFILE's 253 radicals have a name; the other 26 are components no school lists as a radical, and are left out rather than guessed. Keyed by the RADKFILE key, as everything about a radical is, so a stand-in is looked up as written: 汁 answers さんずい.
radicalName(key: string): string | null
The Japanese name of a RADKFILE radical, in hiragana, or null where no school names it.
radicalNameCount(): number
How many of the radicals carry a Japanese name.
radicalNameEntry(key: string): RadicalNameEntry | null
Everything Kanji alive says about a RADKFILE radical: its name, romaji, English meaning, position and Kangxi number. Null where it says nothing.
type RadicalNameEntry = { /** The name, in hiragana. */ readonly name: string; readonly romaji: string; /** Kanji alive's English meaning, which is also the classical name. */ readonly meaning: string; /** Where the shape sits in a character (へん, つくり, かんむり...), or null. */ readonly position: string | null; /** The Kangxi radical number the shape belongs to. */ readonly kangxi: number | null; };
WHAT A JAPANESE SCHOOL CALLS A RADICAL: 氵 is さんずい, 辶 is しんにょう, 艹 is くさかんむり. The names a learner hears from a teacher, which are neither the dictionary's English ("water", "walk", "grass") nor any course's mnemonics. From Kanji alive's table of the 214 Kangxi radicals and their variants (CC BY 4.0, credit in NOTICE.md), joined to RADKFILE's 253 by shape. 227 have a name; the rest are components no school lists as a radical (九, 乞, 久, 井), and a caller gets null for those, never a guess.
Keyed by the RADKFILE key, as everything about a radical is, so a stand-in is looked up as written: 汁 answers さんずい. The names are a plain import, so a page that draws a radical's name draws it with no request.
radicalNameForShape(shape: string): string | null
The Japanese name of a radical drawn as this shape, or null. 阝 is on both sides of a character and the first of its two names answers (おおざと).
@johnmorrisdotca/bushu/strokesKANJI_STROKES sortByStrokes strokesOf
KANJI_STROKES: { source: { name: string; url: string; fileVersion: string | null; databaseVersion: string | null; fileDate: string | null; sha256: string; retrieved: string; }; byStrokes: Readonly<Record<string, string>>; }
THE STROKE COUNT OF EVERY KANJI IN THE RADICAL INDEX, from KANJIDIC2. Written by scripts/build-data.mjs, never edited by hand; the monthly refresh (.github/workflows/radkfile-refresh.yml) runs the script again.
KANJIDIC2 is the property of the Electronic Dictionary Research and Development Group (EDRDG), used under the Creative Commons Attribution-ShareAlike 4.0 licence and the Group's conditions: https://www.edrdg.org/edrdg/licence.html. This data is derived from it and is under the same licence (see NOTICE.md).
6355 kanji, grouped by their stroke count so that a count is one string; where KANJIDIC2 lists more than one count for a kanji, this is the first, the accepted one.
sortByStrokes(kanji: readonly string[]): string[]
The kanji with the fewest strokes first; kanji with the same count keep the order they were given in, and a character with no count goes last. A new array: what was given is not changed.
strokesOf(kanji: string): number | null
The strokes in a kanji, or null for a character the index does not hold.
@johnmorrisdotca/bushu/pickerBUSHU_RESULT_LIMIT BUSHU_STRINGS BUSHU_STYLE BushuEventDetail BushuLanguage bushuLanguageOf BushuMount BushuMountOptions BushuNameEntry bushuSay ensureBushuStyle loadBushuData mountBushu
BUSHU_RESULT_LIMIT: 120
The most matches a picker draws unless limit says.
BUSHU_STRINGS: Record<BushuLanguage, Record<string, string>>
BUSHU_STYLE: "\n.bushu {\n --bp-ink: #1f2320; --bp-muted: #6b6f68; --bp-rule: #ddd6c6; --bp-surface: #fbf8f1; --bp-chosen: #2f5d4a; --bp-chosen-ink: #f3efe4; --bp-accent: #b5452c;\n display: block; max-width: 100%; box-sizing: border-box; color: var(--bp-ink); container-type: inline-size;\n font-family: \"Hiragino Sans\", \"Yu Gothic\", \"Noto Sans CJK JP\", \"Noto Sans JP\", system-ui, -apple-system, \"Segoe UI\", …
THE STYLE a Bushu picker wears (mountBushu, <bushu-picker>): its colours as custom properties on .bushu, its boxes, its tiles and its chips. Colours are --bp-ink, --bp-muted, --bp-rule, --bp-surface, --bp-chosen, --bp-chosen-ink and --bp-accent, so a page sets only the ones it wants different. Light and dark follow the page's (prefers-color-scheme, or a data-theme on the root).
Nothing moves when something is chosen: the line of chosen parts, the line of words, the list of matches, the kanji's card and the grid each keep one height whatever is in them, a long list scrolls inside its box, and every button is at least 44 pixels square. The tiles cannot be selected; the kanji in the card can, so that it can be copied.
type BushuEventDetail = { /** The parts chosen, as RADKFILE's keys, in the grid's order. */ chosen: string[]; /** How many kanji hold all of them. */ matches: number; /** The kanji whose card is open, or null. */ kanji: string | null; /** The parts of that kanji, as RADKFILE's keys, fewest strokes first. */ parts: string[]; };
What a picker tells of itself, in bushu-change and bushu-pick events and to the callbacks.
type BushuLanguage = "en" | "ja";
THE WORDS BUSHU SAYS, in English and Japanese: what the picker's buttons and lines of words say, and what a screen reader hears. Plain data, so a page can read them, replace a few, or add a language of its own beside these two.
{name} in a line is a value filled in; a line foo that has a fooOne beside it is said as fooOne when its {n} is 1.
bushuLanguageOf(tag: string | null | undefined): BushuLanguage
The language a lang attribute asks for: Japanese for any ja, English for everything else.
type BushuMount = { /** Choose a part, by its RADKFILE key or by its shape (氵 for 汁), or take it away if it is chosen. A part that cannot narrow anything, or is not in the index, is left alone. */ toggle(radical: string): void; /** Take a part away, by its key or its shape. */ remove(radical: string): void; /** Take every part away. */ clear(): void; /** Open the card of a kanji, with its parts; null closes it. */ s…
What mountBushu gives back: methods for everything a button does, and destroy.
type BushuMountOptions = { /** The radicals: `RADKFILE.radicals` from `@johnmorrisdotca/bushu/radkfile`. */ radicals: readonly Radical[]; /** The Japanese name and English meaning of a radical by its key, or null: `radicalNameEntry` from `@johnmorrisdotca/bushu/names`. Without it no names are shown. */ names?: (key: string) => BushuNameEntry | null; /** The strokes in a kanji, or null: `strokesOf` from `@johnmorrisd…
type BushuNameEntry = { readonly name: string; readonly meaning: string };
The names a picker shows beside a radical: what radicalNameEntry from /names returns.
bushuSay(language: BushuLanguage, key: string, values?: Record<string, string | number>): string
A line in a language, with its {name} values filled in, and its One form when n is 1.
ensureBushuStyle(document?: Document): void
Puts the picker's style in the page once. Called by mountBushu; call it yourself to share one style with your own markup.
loadBushuData(): Promise<Pick<BushuMountOptions, "radicals" | "names" | "strokes">>
Loads the data a picker shows, each module at the moment it is asked for, so that a page which never opens a picker never loads it: the radicals, the Japanese names and the strokes of a kanji. Hand the result to mountBushu:
const data = await loadBushuData();
mountBushu(element, { ...data, language: "ja" });
mountBushu(host: HTMLElement, options: BushuMountOptions): BushuMount
@johnmorrisdotca/bushu/elementBushuPicker: typeof BushuPicker
@johnmorrisdotca/bushu/element/defineこの日本語は、まだ日本語を母語とする方の確認を受けていません。訂正を歓迎します。