test+docs: 逐元素標點樣式覆寫——回歸測試(6 例)+ README/ARCHITECTURE 說明
承 c00fb3e(data-jz-style/lang.stylePolicy 之設置格式已推送)。補 e2e 回歸與文檔。 測試(6 例,127 全綠):屬性 banjiao/選擇器 banjiao/屬性優先於選擇器/最近祖先勝/非法值 回退 lang/revert 還原(作者屬性保留)。 文檔:README 設置速查補 lang.stylePolicy + data-jz-style 兩行、新增「逐元素標點樣式」小節 (非段落用半角之選擇器與屬性兩範式);ARCHITECTURE §4.3 補 resolveStyle 優先序(逐元素覆寫 > policy > style > 內建)。
This commit is contained in:
+8
-4
@@ -326,11 +326,15 @@ avoid」用組合入口 `isAvoided`(=自身或祖先,階段二歸一,免
|
||||
- `effectiveLang(node)`:最近祖先 `lang`(小寫),無則回退 `options.lang.default`。
|
||||
- `classify(lang)`:歸為 `zh-Hans` / `zh-Hant`(含 `zh-TW`/`zh-HK`)/ `ja` / 其他;
|
||||
BCP-47 前綴邊界比對(`zh` 命中 `zh-Hant`,不命中 `zhx`)。
|
||||
- `style(lang)`:回傳該語言之標點寬度政策——`quanjiao`(全角式)|`kaiming`
|
||||
(開明式)|`banjiao`(半角式)。預設:`zh-Hant`/`zh-HK` → 全角式;`zh-Hans`
|
||||
→ 開明式;可由 `options.lang.policy` 覆寫(即你要的「依 lang 自由選擇」)。
|
||||
- `resolveStyle(node)`:回傳節點之有效標點寬度政策——`quanjiao`(全角式)|`kaiming`
|
||||
(開明式)|`banjiao`(半角式)。**優先序**:**逐元素覆寫**(`resolveStyleOverride`:自身及
|
||||
祖先鏈掃 `data-jz-style` 屬性/`stylePolicy` 選擇器命中,近祖先勝、同層屬性優先於選擇器)
|
||||
> 逐 lang `policy` > 全域 `style` > 內建逐語言預設(`zh-Hant`/`zh-HK` → 全角、`zh-Hans` →
|
||||
開明)。逐元素覆寫供「非段落/UI 元素用半角、段落用全角/開明」等需求(`data-jz-style` 屬性
|
||||
值非法則忽略、回退 lang)。此純為「按元素選樣式」之路由,`banjiao` 半形渲染沿用 `halt`/
|
||||
`margin`(§6.3),無新增壓縮邏輯。
|
||||
|
||||
CSS 端對應以 `:lang()` 選擇器落實同一政策(I2/I3)。
|
||||
CSS 端對應以 `:lang()` 選擇器落實 lang 層政策(I2/I3);逐元素覆寫屬 JS 端 baseline 判定。
|
||||
|
||||
### 4.4 `dom.ts`——`jz-*` 元素工廠與還原
|
||||
|
||||
|
||||
@@ -115,13 +115,14 @@ createJuzhen({
|
||||
`pillSelector`/`isolateSelector`/`scope.*`(**CSS 選擇器**,可用 class/屬性)**不同機制**,
|
||||
勿混。斜體「詳見」指向本文檔展開該項之小節。
|
||||
|
||||
**語言與擠壓政策**(優先序:`policy` > `style` > 逐語言內建)
|
||||
**語言與擠壓政策**(優先序:**逐元素** `data-jz-style`/`stylePolicy` > `policy` > `style` > 逐語言內建)
|
||||
|
||||
| 選項 | 型別 | 預設 | 作用 |
|
||||
|---|---|---|---|
|
||||
| `lang.default` | string | `"zh-Hant"` | 無 `lang` 祖先時之預設語言標籤 |
|
||||
| `lang.style` | `quanjiao|kaiming|banjiao` | 逐語言內建(繁→全角、簡→開明) | **全域政策一鍵覆寫** |
|
||||
| `lang.policy` | `Record<lang, style>` | `{}` | 逐 lang 政策(最高優先) |
|
||||
| `lang.policy` | `Record<lang, style>` | `{}` | 逐 lang 政策 |
|
||||
| `lang.stylePolicy` | `{selector,style}[]` | `[]` | **逐元素樣式覆寫**(選擇器命中之子樹用指定樣式;如 `[{selector:"h1,button,.ui",style:"banjiao"}]`);配合 `data-jz-style` 屬性,優先於 lang |
|
||||
|
||||
**作用域**
|
||||
|
||||
@@ -162,6 +163,7 @@ createJuzhen({
|
||||
| 屬性 | 值 | 作用 |
|
||||
|---|---|---|
|
||||
| `data-jz-level` | `text|paragraph` | 逐子樹功能分級 |
|
||||
| `data-jz-style` | `quanjiao|kaiming|banjiao` | 逐子樹標點樣式覆寫(近祖先勝、優先於選擇器與 lang) |
|
||||
| `data-juzhen` | 功能名(空格分隔) | 白名單:僅列出之功能生效 |
|
||||
| `data-juzhen-off` | 功能名 | 黑名單:停用列出之功能 |
|
||||
| `data-jz-skip` | (存在即可) | 標記元素為 avoid(內外皆不處理) |
|
||||
@@ -170,6 +172,32 @@ createJuzhen({
|
||||
**預留、當前無效果**:`hanging`(已移除,見下);`biaodian`/`emphasis`/`ruby`(架構預留切片
|
||||
#7/#8)。注意頂層 `biaodian`(功能開關)與 `charClass.biaodian`(標點子類擴充表)**同名異義**。
|
||||
|
||||
### 逐元素標點樣式(`data-jz-style`/`stylePolicy`)
|
||||
|
||||
標點樣式(`quanjiao` 全角式/`kaiming` 開明式/`banjiao` 半角式)預設由 `lang` 決定,但可
|
||||
**逐元素/逐範圍覆寫**——典型:**非段落/UI 元素(標題、按鈕)用半角式,段落用全角/開明**。
|
||||
|
||||
```ts
|
||||
// 選擇器批量:h1/h2/button 及 .ui 子樹用半角式,其餘(段落)走 lang
|
||||
createJuzhen({
|
||||
lang: {
|
||||
default: "zh-Hant", // 段落 → 全角式(繁)
|
||||
stylePolicy: [ { selector: "h1, h2, button, .ui", style: "banjiao" } ],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```html
|
||||
<!-- 或以 HTML 屬性逐元素/逐範圍標記(標於元素或任一祖先,該子樹生效) -->
|
||||
<h1 data-jz-style="banjiao">標題:半角標點更緊湊</h1>
|
||||
<p>段落沿用 lang 之全角/開明式。</p>
|
||||
```
|
||||
|
||||
優先序:**逐元素覆寫**(`data-jz-style` 屬性/`stylePolicy` 選擇器,**近祖先勝、同層屬性優先於
|
||||
選擇器**)> `lang.policy` > `lang.style` > 內建逐語言預設。屬性值非法(拼錯)則忽略、回退
|
||||
lang。半角式渲染沿用既有 `halt`/`margin` 機制(見下節),本功能只做「按元素選樣式」、無新增
|
||||
渲染邏輯。
|
||||
|
||||
### 行內元素三分類:透明/隔離/整體
|
||||
|
||||
行內元素對排版有且僅有三種口徑。**邏輯文本流之遍歷原語**(中西間隙、擠壓相鄰、禁則綁定、
|
||||
|
||||
@@ -192,6 +192,74 @@ test("jiya:全角式(zh-Hant)baseline 一律不收(孤立標點無 jz-ha
|
||||
assert.equal(countTag(document.querySelector("p"), "jz-char.jz-half"), 0, "全角式不擠(無相鄰標點)");
|
||||
});
|
||||
|
||||
// ----- 逐元素標點樣式覆寫(data-jz-style 屬性 / lang.stylePolicy 選擇器)-----
|
||||
|
||||
test("樣式覆寫:data-jz-style=\"banjiao\" 令該子樹句末點號半形,p 仍走 lang 之全角", () =>
|
||||
{
|
||||
const { document } = setupDom(
|
||||
'<div lang="zh-Hant"><h1 data-jz-style="banjiao">甲。乙</h1><p>丙。丁</p></div>',
|
||||
);
|
||||
createJuzhen({ spacing: false, longWord: false, jinze: false }).render(document.body);
|
||||
assert.ok(document.querySelector("h1 jz-char.bd-stop").classList.contains("jz-half"),
|
||||
"h1(banjiao)句末點號半形");
|
||||
assert.ok(!document.querySelector("p jz-char.bd-stop").classList.contains("jz-half"),
|
||||
"p(zh-Hant 全角式)句末點號不收");
|
||||
});
|
||||
|
||||
test("樣式覆寫:lang.stylePolicy 選擇器命中之元素用指定樣式(h2→banjiao)", () =>
|
||||
{
|
||||
const { document } = setupDom('<div lang="zh-Hans"><h2>甲。乙</h2><p>丙。丁</p></div>');
|
||||
createJuzhen({
|
||||
spacing: false, longWord: false, jinze: false,
|
||||
lang: { default: "zh-Hant", stylePolicy: [ { selector: "h2", style: "banjiao" } ] },
|
||||
}).render(document.body);
|
||||
assert.ok(document.querySelector("h2 jz-char.bd-stop").classList.contains("jz-half"),
|
||||
"h2(stylePolicy banjiao)句末點號半形");
|
||||
assert.ok(!document.querySelector("p jz-char.bd-stop").classList.contains("jz-half"),
|
||||
"p(zh-Hans 開明式)句末點號不收");
|
||||
});
|
||||
|
||||
test("樣式覆寫:屬性優先於選擇器(data-jz-style=\"quanjiao\" 覆蓋 stylePolicy 之 banjiao)", () =>
|
||||
{
|
||||
const { document } = setupDom('<h1 lang="zh-Hant" data-jz-style="quanjiao">甲。乙</h1>');
|
||||
createJuzhen({
|
||||
spacing: false, longWord: false, jinze: false,
|
||||
lang: { stylePolicy: [ { selector: "h1", style: "banjiao" } ] },
|
||||
}).render(document.body);
|
||||
assert.ok(!document.querySelector("jz-char.bd-stop").classList.contains("jz-half"),
|
||||
"屬性 quanjiao 勝出、句末點號不收");
|
||||
});
|
||||
|
||||
test("樣式覆寫:最近祖先勝(內層 data-jz-style 覆蓋外層)", () =>
|
||||
{
|
||||
const { document } = setupDom(
|
||||
'<div lang="zh-Hant" data-jz-style="banjiao"><span data-jz-style="quanjiao">甲。乙</span></div>',
|
||||
);
|
||||
createJuzhen({ spacing: false, longWord: false, jinze: false }).render(document.body);
|
||||
assert.ok(!document.querySelector("jz-char.bd-stop").classList.contains("jz-half"),
|
||||
"內層 span quanjiao 勝過外層 div banjiao、不收");
|
||||
});
|
||||
|
||||
test("樣式覆寫:非法屬性值忽略、回退 lang", () =>
|
||||
{
|
||||
const { document } = setupDom('<p lang="zh-Hans" data-jz-style="bogus">甲,乙</p>');
|
||||
createJuzhen({ spacing: false, longWord: false, jinze: false }).render(document.body);
|
||||
assert.ok(document.querySelector("jz-char.bd-pause").classList.contains("jz-half"),
|
||||
"非法值忽略、回退 zh-Hans 開明式(句內點號 ,半形)");
|
||||
});
|
||||
|
||||
test("樣式覆寫 revert:data-jz-style 為作者屬性,render→revert 文字與屬性還原", () =>
|
||||
{
|
||||
const { document } = setupDom('<h1 lang="zh-Hant" data-jz-style="banjiao">標題。內容</h1>');
|
||||
const jz = createJuzhen({ spacing: false, longWord: false, jinze: false });
|
||||
jz.render(document.body);
|
||||
jz.revert(document.body);
|
||||
const h1 = document.querySelector("h1");
|
||||
assert.equal(h1.textContent, "標題。內容");
|
||||
assert.equal(h1.getAttribute("data-jz-style"), "banjiao", "作者屬性保留");
|
||||
assert.equal(countTag(document.body, "jz-char"), 0);
|
||||
});
|
||||
|
||||
test("jiya 機制 A:句末標點後接閉引號,前者半形(。」→ 。jz-half,全角亦然)", () =>
|
||||
{
|
||||
const { document } = setupDom('<p lang="zh-Hant">好。」嗎</p>');
|
||||
|
||||
Reference in New Issue
Block a user