pt —— パススルー属性の結合
コンポーネント内部には複数の要素があります(ボタンの <button> とその内側の <span>、入力フィールドの <input> と先頭アイコン <i> など)。ユーザーが外部からそのうちの 1 つを微調整したい場合、どうすればよいでしょうか? lh-ui の答えは PT(PassThrough)属性パックです:コンポーネントは pt prop を公開し、内部要素の名前ごとに class / style / attrs をそれぞれパススルーします。mergePt がそれをコンポーネント自身のベースクラス名と結合する役目を担います。
- コンポーネントライブラリ内部:各内部要素に対して
mergePt(props.pt?.icon, 'lh-button__icon')を呼び、そこに付けるべき class / style / attrs を得る。 - ユーザー側は
ptprop を通じてコンポーネント内部に踏み込んで微調整し、:deep()で貫く必要がない。
使うべきでない:テーマレベルのカスタマイズ(themeVars を使う);単なるクラス名の追加(class prop を使う)。
インポート方法
Section titled “インポート方法”import { mergePt, type LhPtAttrs } from '@lh-ui/share/core/shared/pt'export interface LhPtAttrs { class?: string | string[] | Record<string, boolean> style?: Record<string, string> attrs?: Record<string, any>}| 関数 | シグネチャ | 役割 |
|---|---|---|
mergePt |
(pt: LhPtAttrs | undefined, baseClass: string) => { class: string[]; style: Record<string, string>; attrs: Record<string, any> } |
PT 属性とベースクラス名を結合 |
baseClass は常に配列の先頭で、ユーザーの class は後ろです:
mergePt({ class: 'my-icon' }, 'lh-button__icon')// { class: ['lh-button__icon', 'my-icon'], style: {}, attrs: {} }
mergePt({ class: ['a', 'b'] }, 'lh-button__icon')// class: ['lh-button__icon', 'a', 'b']
mergePt({ class: { active: true, disabled: false } }, 'lh-button__icon')// class: ['lh-button__icon', 'active'] —— オブジェクトは値が真のキーだけを取る
mergePt({ class: 'a b ' }, 'lh-button__icon')// class: ['lh-button__icon', 'a', 'b'] —— 文字列は空白で分割し、空文字を除外するなぜ baseClass を先にするのか:BEM ベースクラス名の特異度がユーザーのクラス名に圧倒されてはならないからです(同じく単一クラス名の場合、後ろに書いたものが先に書いたものを上書きする)。コンポーネントの --primary モディファイアは、依然としてユーザーの my-icon に勝っていなければなりません。
style / attrs
Section titled “style / attrs”mergePt(undefined, 'lh-button__icon')// { class: ['lh-button__icon'], style: {}, attrs: {} } —— pt が空のときはすべて空
mergePt({ style: { color: 'red' }, attrs: { 'data-x': '1' } }, 'lh-button__icon')// { class: ['lh-button__icon'], style: { color: 'red' }, attrs: { 'data-x': '1' } }注意:style / attrs は参照のパススルーで、コピーは行いません。UI 層は受け取った後そのまま v-bind="merged.attrs" / :style="merged.style" できます;コンポーネント内部で戻り値を mutate しないでください。
コンポーネント内部(ボタンのアイコン要素)
Section titled “コンポーネント内部(ボタンのアイコン要素)”// button コンポーネントconst iconPt = computed(() => mergePt(props.pt?.icon, 'lh-button__icon'))<template> <button class="lh-button"> <i v-if="icon" v-bind="iconPt.attrs" :class="iconPt.class" :style="iconPt.style" /> <span><slot /></span> </button></template><lh-button :pt="{ icon: { class: 'text-lg', style: { marginRight: '4px' } }, $root: { attrs: { 'data-test': 'submit' } }, }"> 送信</lh-button>const iconPt = mergePt(pt?.icon, 'lh-button__icon')
return ( <button className="lh-button"> {icon && <i className={iconPt.class.join(' ')} style={iconPt.style} {...iconPt.attrs} />} </button>)- 浅い結合で、deepMerge はしない:
classは配列レベルで継ぎ接ぎし、style/attrsはパック全体を差し替える。ここで深い結合にセマンティクスはない(class はツリーではなくリストである)。 ptが空のときは全空のオブジェクトを返す:UI 層は何も考えず分解でき、空判定が不要。- 戻り値の class は配列:Vue の
:classは配列を受け取るが、React は.join(' ')が必要。 - ベースクラス名は省略できない:
mergePt(pt, '')は先頭が空文字の配列になってしまう——BEM のベースクラス名はスタイルのアンカーであり、各内部要素に必ずなければならない。
@lh-ui/share/core/pt → mergePt( )返回值
(填好参数后点运行)Copyright © 2024-2026 FengMo WenXue