PluginProbe ʕ •ᴥ•ʔ
VK Blocks / 1.125.0
VK Blocks v1.125.0
1.126.4 1.126.3 1.126.1 1.126.2 1.126.0 1.125.0 1.124.0 1.123.0 1.122.0 1.121.0 1.120.0 1.119.2 1.119.1 1.119.0 1.118.7 1.82.0.0 1.82.0.1 1.83.0.0 1.83.0.1 1.84.0.0 1.84.0.1 1.85.0.2 1.85.0.3 1.85.1.0 1.85.1.1 1.86.0.0 1.86.0.1 1.86.1.0 1.87.0.0 1.87.0.1 1.88.0.1 1.88.0.2 1.89.0.0 1.9.2 1.90.0.0 1.90.0.1 1.90.1.1 1.91.0.0 1.91.1.1 1.92.0.0 1.92.0.1 1.92.1.0 1.92.1.1 1.93.0.0 trunk 1.93.0.1 0.1.1 1.93.1.0 0.10.3 1.93.1.1 0.11.0 1.94.0.0 0.12.6 1.94.0.1 0.15.1 1.94.1.0 0.16.2 1.94.2.1 0.17.6 1.94.2.2 0.2.2 1.95.0.3 0.22.4 1.95.0.4 0.26.7 1.96.2.0 0.3.2 1.96.2.1 0.31.0 1.97.0.1 0.35.5 1.97.0.2 0.37.5 1.99.0.0 0.38.8 1.99.0.1 0.39.4 0.4.7 0.41.0 0.42.0 0.44.13 0.45.2 0.46.1 0.47.0 0.48.0 0.49.8 0.5.2 0.50.3 0.51.0 0.52.2 0.53.2 0.54.0 0.55.0 0.56.2 0.56.3 0.57.6 0.58.10 0.59.0 0.6.0 0.60.1 0.61.2 0.7.1 0.8.2 1.0.16 1.10.0 1.100.0.0 1.100.0.1 1.102.0.0 1.102.0.1 1.103.0.0 1.104.0.0 1.104.0.1 1.105.1.0 1.105.1.1 1.106.0.0 1.106.0.1 1.107.0.0 1.107.0.1 1.107.0.2 1.108.0.0 1.108.0.1 1.109.0.0 1.109.0.1 1.11.4 1.110.0.0 1.110.0.1 1.111.0.0 1.111.0.1 1.111.0.2 1.112.0.0 1.112.0.1 1.113.0.0 1.113.0.1 1.114.0.0 1.114.0.1 1.114.2.0 1.114.2.1 1.115.0.0 1.115.0.1 1.115.1.0 1.115.1.1 1.115.2.0 1.115.2.1 1.116.0.0 1.116.0.1 1.116.1.0 1.116.1.1 1.116.2.0 1.117.0.0 1.117.0.1 1.117.1.0 1.118.2 1.118.5 1.12.0 1.13.2 1.14.1 1.15.1 1.16.11 1.17.0 1.18.6 1.19.0 1.19.1 1.2.3 1.20.7 1.21.0 1.22.4 1.23.0 1.24.5 1.25.1 1.26.2 1.27.7.2 1.28.0.1 1.29.2.0 1.3.9 1.30.0.1 1.31.0.1 1.32.0.2 1.33.2.1 1.36.1.5 1.37.0.0 1.39.2.1 1.4.6 1.40.0.1 1.40.1.0 1.40.1.1 1.41.0.0 1.41.0.1 1.41.2.2 1.41.2.3 1.43.0.0 1.43.0.1 1.43.0.2 1.44.0.0 1.44.0.1 1.45.0.0 1.45.0.1 1.46.0.0 1.46.0.1 1.47.0.0 1.47.0.1 1.47.1.0 1.48.0.0 1.48.0.1 1.48.0.2 1.48.1.0 1.48.1.1 1.5.0 1.50.0.0 1.50.0.1 1.51.0.0 1.51.0.1 1.52.0.0 1.52.0.1 1.53.0.0 1.53.0.1 1.54.0.0 1.54.0.1 1.55.0.0 1.55.0.1 1.56.0.0 1.56.0.1 1.57.0.0 1.57.0.2 1.57.0.3 1.57.0.4 1.57.0.5 1.57.1.0 1.57.1.1 1.57.1.2 1.58.0.0 1.58.0.1 1.59.0.0 1.59.0.1 1.6.0 1.60.0.0 1.60.0.1 1.63.0.0 1.63.0.1 1.64.0.0 1.64.0.1 1.64.1.0 1.64.1.2 1.67.0.0 1.67.0.1 1.68.0.0 1.68.0.1 1.69.0.0 1.69.0.1 1.69.1.1 1.69.1.2 1.7.1 1.70.0.0 1.70.0.1 1.71.0.0 1.71.0.1 1.72.0.0 1.72.1.0 1.72.1.1 1.73.0.0 1.73.0.1 1.74.0.0 1.74.0.1 1.75.0.0 1.75.1.0 1.75.1.1 1.76.0.0 1.76.0.1 1.76.1.0 1.76.1.1 1.76.2.0 1.76.2.1 1.77.0.0 1.77.0.1 1.78.0.0 1.78.0.1 1.79.0.0 1.79.0.1 1.79.0.2 1.79.0.3 1.79.1.0 1.79.1.1 1.8.2 1.80.1.0 1.80.1.1 1.80.1.2 1.81.0.1 1.81.0.2
vk-blocks / src / blocks / button / edit.js
vk-blocks / src / blocks / button Last commit date
deprecated 2 weeks ago block.json 2 weeks ago component.js 2 weeks ago edit.js 2 weeks ago icon.svg 3 months ago index.js 2 months ago index.php 3 months ago save.js 2 weeks ago style.scss 2 months ago transforms.js 3 months ago
edit.js
1356 lines
1 import { __ } from '@wordpress/i18n';
2 import { VKBButton } from './component';
3 import { FontAwesome } from '@vkblocks/utils/font-awesome-new';
4 import {
5 SelectControl,
6 PanelBody,
7 BaseControl,
8 TextControl,
9 Button,
10 RangeControl,
11 __experimentalToggleGroupControl as ToggleGroupControl,
12 __experimentalToggleGroupControlOption as ToggleGroupControlOption,
13 ToolbarGroup,
14 __experimentalUnitControl as UnitControl, // eslint-disable-line @wordpress/no-unsafe-wp-apis
15 } from '@wordpress/components';
16 import {
17 RichText,
18 InspectorControls,
19 useBlockProps,
20 BlockControls,
21 } from '@wordpress/block-editor';
22 import { settings } from '@wordpress/icons';
23 import { useEffect, useState } from '@wordpress/element';
24 import { useInstanceId } from '@wordpress/compose';
25 import { dispatch, select } from '@wordpress/data';
26 import { AdvancedColorPalette } from '@vkblocks/components/advanced-color-palette';
27 import { isHexColor } from '@vkblocks/utils/is-hex-color';
28 import {
29 isParentReusableBlock,
30 hasBlockIdCollision,
31 shouldUpdateBlockId,
32 } from '@vkblocks/utils/is-parent-reusable-block';
33 import LinkToolbar from '@vkblocks/components/link-toolbar';
34 import { iconLabel } from '@vkblocks/utils/icon-label';
35 import { fixBrokenUnicode } from '@vkblocks/utils/fixBrokenUnicode';
36 import { toPresetSpacingVar } from '@vkblocks/utils/to-preset-spacing-var';
37
38 export default function ButtonEdit(props) {
39 const { attributes, setAttributes, clientId, context, isSelected } = props;
40 const buttonSizeLabelId = useInstanceId(ButtonEdit, 'vk-button-size-label');
41 // プリセット⇔数値指定の切り替えをスクリーンリーダーへ通知するための文言。
42 // onChange ハンドラ�
43 でのみ更新する(既存データを開いた瞬間に dirty 化させないため)。
44 // Live-region message announced to screen readers when switching between the preset
45 // and the numeric size. Updated only inside onChange handlers so opening existing
46 // data never marks the block dirty.
47 const [fontSizeAnnouncement, setFontSizeAnnouncement] = useState('');
48 // 数値�
49 �力(UnitControl)を強制再マウントするためのキー。
50 // UnitControl は�
51 部に�
52 �力ドラフトを持つ制御コンポーネントで、ドラフトの
53 // クランプ結果が現在の保存値(value prop)と等しいと、属性が変化せず再レンダリングが
54 // 起きず、�
55 部ドラフト(例: '1')value('10px')へ再同期されず表示だけ取り残される。
56 // blur 確定時に「ドラフト由来の確定値と保存値が乖離していたら」このキーを更新して
57 // UnitControl だけを作り直し、表示を保存値へ揃える。再マウントは blur 後にだけ
58 // 起こすため、�
59 �力中のフォーカスは奪わない。
60 // Key used to force-remount the numeric input (UnitControl).
61 // UnitControl is a controlled component with an internal input draft; when the
62 // clamped draft equals the current stored value (the value prop), the attribute
63 // does not change, no re-render happens, and the internal draft (e.g. '1') is
64 // never re-synced to value ('10px') — so the display is left stale. On blur, if
65 // the committed value diverges from the stored value, we bump this key to rebuild
66 // only the UnitControl and snap its display to the stored value. The remount fires
67 // only after blur, so it never steals focus while the user is typing.
68 const [fontSizeInputKey, setFontSizeInputKey] = useState(0);
69 // 数値指定が優�
70 �されたことを通知する文言(aria-live でのみ使用)。
71 // 視覚的な注記はコア FontSizePicker 同様に廃し、セグメントを隠す構成へ
72 // 変えたため不要になったが、スクリーンリーダー向けのアナウンスは残す。
73 // Text announced (via aria-live only) when the numeric size takes priority.
74 // The visual note is dropped to match the core FontSizePicker (the preset
75 // segment is hidden instead), but the screen-reader announcement is kept.
76 const fontSizePriorityMessage = __(
77 'The numeric font size takes priority.',
78 'vk-blocks'
79 );
80 const {
81 content,
82 subCaption,
83 buttonUrl,
84 buttonTarget,
85 relAttribute,
86 linkToPost,
87 linkToCustomField,
88 linkCustomFieldName,
89 buttonSize,
90 buttonType,
91 buttonEffect,
92 buttonColor,
93 buttonTextColorCustom,
94 buttonColorCustom,
95 buttonAlign,
96 buttonWidthMobile,
97 buttonWidthTablet,
98 buttonWidth,
99 outerGap,
100 fontAwesomeIconBefore,
101 fontAwesomeIconAfter,
102 iconSizeBefore,
103 iconSizeAfter,
104 buttonHoverBgColorCustom,
105 buttonHoverTextColorCustom,
106 blockId,
107 old_1_31_0,
108 fontSizeValue,
109 } = attributes;
110
111 const isDescendentOfQueryLoop =
112 typeof context?.queryId === 'number' &&
113 Number.isFinite(context.queryId);
114
115 // 親ブロックが vk-blocks/button-outer かどうか判定
116 const parents = select('core/block-editor').getBlockParentsByBlockName(
117 clientId,
118 ['vk-blocks/button-outer']
119 );
120 const isInnerButton = parents.length ? true : false;
121
122 // 「カスタムサイズモード」の表示状�
123 �。コアの FontSizePicker と同様、
124 // 数値�
125 �力欄はトグルで出し�
126 �れする。これは属性ではなく UI のローカル状�
127 �なので
128 // ブロックを dirty 化しない。既存データで fontSizeValue があれば custom モードで開く。
129 // Local UI state for the custom-size mode. As with the core FontSizePicker,
130 // the numeric input is toggled in/out. This is UI-only state (not an attribute),
131 // so it never marks the block dirty. Existing data that has a fontSizeValue
132 // opens in custom mode.
133 const [isCustomSize, setIsCustomSize] = useState(!!fontSizeValue);
134
135 // スライダー(RangeControl)に渡す数値。fontSizeValue は単位付き文字列
136 // ('53px' 形式・レガシーで '1.2em' 等もありうる)なので、parseFloat で
137 // 数値部だけ取り出してスライダー位置に反映する。falsy / 解釈不能な値は
138 // undefined を渡し、マウントしただけでは onChange を発火させない
139 // (=触るまで保存値を書き換えない)。max(100) を�
140 えるレガシー値は
141 // 表示だけ 100 で頭打ちにし、state(fontSizeValue) 自体は書き換えない。
142 // Numeric value passed to the slider (RangeControl). fontSizeValue is a
143 // unit-bearing string ('53px'; legacy values such as '1.2em' are possible),
144 // so parseFloat extracts the numeric part for the slider position. For
145 // falsy / unparsable values, undefined is passed and no onChange fires on
146 // mount (the stored value is not rewritten until the user edits it).
147 // Legacy values above max(100) are clamped to 100 for display only; the
148 // stored fontSizeValue itself is left untouched.
149 const parsedFontSize = fontSizeValue
150 ? parseFloat(fontSizeValue)
151 : undefined;
152 const sliderFontSizeValue =
153 parsedFontSize !== undefined && !Number.isNaN(parsedFontSize)
154 ? Math.min(parsedFontSize, 100)
155 : undefined;
156 // 現在の単位を判定する。px(または値が空=既定 px)ではスライダーを px レンジで、
157 // em / rem 等の相対単位では相対レンジ(0〜10・0.1 刻み)で動かす(コアの
158 // FontSizePicker と同じ考え方)。単位は表示用の派生値で、保存値は書き換えない。
159 // Determine the current unit. For px (or empty, defaulting to px) the slider
160 // uses the px range; for relative units (em / rem) it uses a relative range
161 // (0–10, step 0.1), mirroring the core FontSizePicker. The unit is a derived
162 // value for display only and never rewrites the stored value.
163 const currentFontUnit = (() => {
164 if (!fontSizeValue) {
165 return 'px';
166 }
167 const unitMatch = String(fontSizeValue).match(/[a-z%]+$/i);
168 return unitMatch ? unitMatch[0].toLowerCase() : 'px';
169 })();
170 const isPxFontUnit = currentFontUnit === 'px';
171 const sliderFontMin = isPxFontUnit ? 10 : 0;
172 const sliderFontMax = isPxFontUnit ? 100 : 10;
173 const sliderFontStep = isPxFontUnit ? 1 : 0.1;
174
175 // コアの FontSizePicker と同じ単位セット(px / em / rem / vw / vh)。
176 // useCustomUnits フックは古い wp.components 環境では未提供のため、
177 // バージョン非依存の静的�
178 �列で UnitControl にそのまま渡す。
179 // Same unit set as the core FontSizePicker (px / em / rem / vw / vh).
180 // The useCustomUnits hook is absent on older wp.components builds, so we
181 // pass a version-independent static array straight to UnitControl.
182 const fontSizeUnits = [
183 { value: 'px', label: 'px' },
184 { value: 'em', label: 'em' },
185 { value: 'rem', label: 'rem' },
186 { value: 'vw', label: 'vw' },
187 { value: 'vh', label: 'vh' },
188 ];
189
190 // 数値�
191 �力(UnitControl)・スライダー(RangeControl)の双方から呼ぶ�
192 �通更新処理。
193 // 「カスタム値の更新」だけを�
194 い、プリセット復帰や歯車トグルの排他ロジックは
195 // 混ぜない。next は単位付き文字列('24px')または null(=プリセットへ復帰)。
196 // 初回に数値が�
197 �った時は「数値指定優�
198 �」、値が空になった時は「プリセット復帰」を
199 // スクリーンリーダーへアナウンスする(既存挙動を踏襲)。
200 // Shared updater called by both the numeric input (UnitControl) and the
201 // slider (RangeControl). It only handles "updating the custom value" and does
202 // not mix in preset-return or gear-toggle exclusivity logic. `next` is a
203 // unit-bearing string ('24px') or null (return to preset). It announces the
204 // numeric-priority message on the first set, and the preset-return message
205 // when cleared, mirroring the existing behavior.
206 const updateCustomFontSize = (next) => {
207 setAttributes({ fontSizeValue: next });
208 if (next && !fontSizeValue) {
209 setFontSizeAnnouncement(fontSizePriorityMessage);
210 } else if (!next && fontSizeValue) {
211 setFontSizeAnnouncement(
212 __('Returned to the preset size.', 'vk-blocks')
213 );
214 }
215 };
216
217 // 数値�
218 �力(UnitControl)が満たすべき新規�
219 �力レンジ。スライダー(RangeControl)
220 // と同じ min=10 / max=100 / step=1(px 整数)に揃える。
221 // Range the numeric input (UnitControl) must enforce for new input, matching
222 // the slider (RangeControl): min=10 / max=100 / step=1 (integer px).
223 const FONT_SIZE_MIN = 10;
224 const FONT_SIZE_MAX = 100;
225
226 // 数値�
227 �力(UnitControl)専用の onChange ハンドラ。新規�
228 �力をレンジ
229 // (下限10・上限100・px 整数)に揃えつつ、既存ユーザーのレンジ外レガシー値
230 // (例: '200px'・'1.2em')を受動操作で書き換えないことを両立させる。
231 //
232 // 重要: UnitControl(�
233 NumberControl)に min/max を「渡さない」。min/max
234 // 渡すと�
235 部の number inputHTML 制約違反となるレガシー値を、ユーザーが
236 // 何も操作していなくてもフォーカス→ブラーしただけで境界値へクランプし、その値で
237 // onChange を発火させる(@wordpress/components 34.0.0 を確認・実機e2eでも再現:
238 // isPressEnterToChange=false では blur 時 commit 条件が実質 !validity.valid のみ
239 // となり、レンジ外値で commit→クランプ→onChange 伝播し 200px→100px / 1.2em→10px
240 // と保存値が書き換わる=後方互換違反)。
241 // min/max を渡さなければ validity.valid は常に true・isDirty も立たないため、
242 // レガシー値をフォーカス→ブラーしても commit が走らず onChange 自体が発火しない
243 // (=触らなければ保存値は不変)。
244 // そのうえで新規�
245 �力レンジは、ユーザーが実際に編集したときだけ来るこの onChange の
246 // 中で明示的にクランプして�
247 保する。レンジ外レガシー値はユーザーが能動的に編集した
248 // 場合にのみレンジへ丸められる(その時は新仕様適用が妥当)。
249 // IMPORTANT: do NOT pass min/max to UnitControl. With min/max, the native
250 // number input clamps an out-of-range legacy value on blur and fires onChange
251 // even without user edits (verified in @wordpress/components 34.0.0 and
252 // reproduced in e2e), rewriting the stored value and breaking backward
253 // compatibility. Without min/max, validity stays valid and isDirty stays
254 // false, so no commit/onChange fires on passive focus/blur. The new-input
255 // range is instead enforced here, inside onChange, which only fires on actual
256 // user edits.
257 const clampFontSizePx = (raw) => {
258 // step=1(整数)に丸めてから下限/上限へクランプする。
259 const stepped = Math.round(raw);
260 return Math.min(Math.max(stepped, FONT_SIZE_MIN), FONT_SIZE_MAX);
261 };
262 const handleUnitControlFontSize = (value) => {
263 // 空(クリア)はプリセット復帰。
264 if (!value) {
265 updateCustomFontSize(null);
266 return;
267 }
268 const raw = parseFloat(value);
269 // 数値として解釈できない異常値はそのまま既存�
270 �通処理に委ねる(基本到達しない)。
271 if (Number.isNaN(raw)) {
272 updateCustomFontSize(value);
273 return;
274 }
275 // 単位を判定する。px(または単位なし)は新規�
276 �力レンジ(10〜100・整数px)へ
277 // 揃える。em / rem 等の相対単位は、ユーザー指定の数値・単位をそのまま尊重する
278 // (スライダーは単位に応じて範囲が切り替わり、相対単位でも操作できる)。
279 // Determine the unit. px (or no unit) is snapped to the new-input range
280 // (10–100, integer px). Relative units (em / rem) keep the user's value and
281 // unit as-is (the slider switches range per unit and works for them too).
282 const unitMatch = String(value).match(/[a-z%]+$/i);
283 const unit = unitMatch ? unitMatch[0].toLowerCase() : 'px';
284 if (unit === 'px') {
285 updateCustomFontSize(`${clampFontSizePx(raw)}px`);
286 return;
287 }
288 updateCustomFontSize(`${raw}${unit}`);
289 };
290
291 // blur(編集確定)時に、�
292 �力欄の生ドラフトを「保存される確定値」と突き合わせ、
293 // 両�
294 が乖離していたら UnitControl を再マウントして表示を保存値へ揃える。
295 // 例: 既存保存値が '10px' のときにレンジ下限未満の '1' を打つと、確定値は
296 // clamp 結果 '10px' となり属性が変化しない→再レンダリングが起きず�
297 �力欄に '1' が
298 // 残る。この乖離をここで検出してキーを更新し、�
299 �力欄を保存値('10px')に揃える。
300 // 乖離が無い通常�
301 �力では何もしない(不要な再マウントを避ける)。再マウントは
302 // blur 後にしか走らないため、�
303 �力途中のフォーカスは奪わない(後方互換のための
304 // 「触らなければ書き換えない」�
305 保とも独立しており、保存値は変えない)。
306 // On blur (edit committed), compare the raw input draft with the value that
307 // would actually be stored; if they diverge, remount the UnitControl so its
308 // display snaps to the stored value. Example: with a stored value of '10px',
309 // typing '1' (below the lower bound) commits the clamped '10px', so the
310 // attribute is unchanged, no re-render occurs, and the input keeps showing '1'.
311 // We detect that divergence here and bump the key to align the input to the
312 // stored value ('10px'). For ordinary input with no divergence, do nothing
313 // (avoiding needless remounts). Because the remount runs only after blur, it
314 // never steals focus mid-typing; it also leaves the stored value untouched, so
315 // it is independent of the backward-compat "don't rewrite unless touched" rule.
316 const handleUnitControlFontSizeBlur = (event) => {
317 const raw = event?.target?.value;
318 // �
319 �力欄が空(クリア)のときは onChange 側でプリセット復帰済み。何もしない。
320 if (raw === undefined || raw === null || raw === '') {
321 return;
322 }
323 const numeric = parseFloat(raw);
324 if (Number.isNaN(numeric)) {
325 return;
326 }
327 // onChange と同じ規則で「確定後に保存されているはずの値」を組み立てる。
328 const unitMatch = String(raw).match(/[a-z%]+$/i);
329 const unit = unitMatch ? unitMatch[0].toLowerCase() : currentFontUnit;
330 const committed =
331 unit === 'px'
332 ? `${clampFontSizePx(numeric)}px`
333 : `${numeric}${unit}`;
334 // 表示中の生ドラフトを正規化(単位を補ってから)保存確定値と比較する。
335 const normalizedRaw = unitMatch ? String(raw) : `${raw}${unit}`;
336 if (normalizedRaw !== committed) {
337 setFontSizeInputKey((prev) => prev + 1);
338 }
339 };
340
341 // 以前の値を切り替え
342 useEffect(() => {
343 if (attributes.clientId !== undefined) {
344 setAttributes({ clientId: undefined });
345 }
346 // issue #2556: blockId を毎リロードで上書きすると dirty 化するため、
347 // 「未確定」または「再利用ブロック外での実衝突(複製)」のときだけ再採番する。
348 if (
349 shouldUpdateBlockId({
350 blockId,
351 isInReusableBlock: isParentReusableBlock(clientId),
352 hasCollision: hasBlockIdCollision(clientId, blockId),
353 })
354 ) {
355 setAttributes({ blockId: clientId });
356 }
357 if (
358 buttonUrl === null ||
359 buttonUrl === 'null' ||
360 buttonUrl === 'undefined' ||
361 buttonUrl === ''
362 ) {
363 setAttributes({ buttonUrl: undefined });
364 }
365 if (buttonColorCustom === undefined) {
366 setAttributes({ buttonTextColorCustom: undefined });
367 }
368 if (
369 buttonColorCustom === null ||
370 buttonColorCustom === 'null' ||
371 buttonColorCustom === 'undefined' ||
372 buttonColorCustom === ''
373 ) {
374 setAttributes({ buttonColorCustom: undefined });
375 }
376 if (
377 fontAwesomeIconBefore === null ||
378 fontAwesomeIconBefore === 'null' ||
379 fontAwesomeIconBefore === 'undefined' ||
380 fontAwesomeIconBefore === ''
381 ) {
382 setAttributes({ fontAwesomeIconBefore: undefined });
383 }
384 if (
385 fontAwesomeIconAfter === null ||
386 fontAwesomeIconAfter === 'null' ||
387 fontAwesomeIconAfter === 'undefined' ||
388 fontAwesomeIconAfter === ''
389 ) {
390 setAttributes({ fontAwesomeIconAfter: undefined });
391 }
392 if (
393 subCaption === null ||
394 subCaption === 'null' ||
395 subCaption === 'undefined' ||
396 subCaption === ''
397 ) {
398 setAttributes({ subCaption: undefined });
399 }
400 if (old_1_31_0 === undefined) {
401 if (buttonWidthMobile === undefined) {
402 setAttributes({ buttonWidthMobile: buttonWidth });
403 }
404 if (buttonWidthTablet === undefined) {
405 setAttributes({ buttonWidthTablet: buttonWidth });
406 }
407 setAttributes({ old_1_31_0: true });
408 }
409 if (!isInnerButton) {
410 setAttributes({ buttonWidth: 0 });
411 }
412 }, [clientId]);
413
414 const { updateBlockAttributes } = dispatch('core/block-editor');
415
416 // buttonColor が有効なら buttonColorCustom と buttonTextColorCustom を無効化
417 // プルダウンから直接カスタムを選ぶとその瞬間色が適用されなくなるので primary に戻す
418 // buttonColorCustom が有効でないと buttonTextColorCustom は意味を成さないので無効化
419 useEffect(() => {
420 if (buttonColor !== 'custom') {
421 updateBlockAttributes(clientId, {
422 buttonTextColorCustom: undefined,
423 });
424 updateBlockAttributes(clientId, { buttonColorCustom: undefined });
425 } else if (
426 buttonColorCustom === undefined &&
427 buttonColor === 'custom'
428 ) {
429 updateBlockAttributes(clientId, { buttonColor: 'primary' });
430 updateBlockAttributes(clientId, {
431 buttonTextColorCustom: undefined,
432 });
433 }
434 }, [buttonColor]);
435
436 // buttonColorCustom が有効なら buttonColor を custom に
437 // buttonColorCustom が空白かつ buttonColor が custom なら buttonColor を primary に
438 useEffect(() => {
439 if (buttonColorCustom !== undefined) {
440 updateBlockAttributes(clientId, { buttonColor: 'custom' });
441 } else if (buttonColor === 'custom') {
442 // 背景色クリアされたらデフォルトに戻す
443 updateBlockAttributes(clientId, { buttonColor: 'primary' });
444 }
445 }, [buttonColorCustom]);
446
447 useEffect(() => {
448 const fixes = {};
449 if (fontAwesomeIconBefore) {
450 const fixed = fixBrokenUnicode(fontAwesomeIconBefore);
451 if (fixed !== fontAwesomeIconBefore) {
452 fixes.fontAwesomeIconBefore = fixed;
453 }
454 }
455 if (fontAwesomeIconAfter) {
456 const fixed = fixBrokenUnicode(fontAwesomeIconAfter);
457 if (fixed !== fontAwesomeIconAfter) {
458 fixes.fontAwesomeIconAfter = fixed;
459 }
460 }
461 if (Object.keys(fixes).length > 0) {
462 setAttributes(fixes);
463 }
464 }, [fontAwesomeIconBefore, fontAwesomeIconAfter]);
465
466 let containerClass;
467 // カスタムカラーの場合 またはアウターにギャップが指定されている場合 またはホバー色が指定されている場合
468 if (
469 (buttonColorCustom !== undefined && isHexColor(buttonColorCustom)) ||
470 (buttonTextColorCustom !== undefined &&
471 isHexColor(buttonTextColorCustom)) ||
472 outerGap ||
473 (buttonHoverBgColorCustom !== undefined &&
474 buttonHoverBgColorCustom !== '') ||
475 (buttonHoverTextColorCustom !== undefined &&
476 buttonHoverTextColorCustom !== '')
477 ) {
478 containerClass = `vk_button vk_button-color-custom vk_button-${blockId}`;
479 } else {
480 containerClass = `vk_button vk_button-color-custom`;
481 }
482
483 if (isInnerButton) {
484 if (buttonWidthMobile) {
485 // 横並びボタンで�
486 が指定されている
487 containerClass += ` vk_button-width-mobile-${buttonWidthMobile}`;
488 }
489 if (buttonWidthTablet) {
490 containerClass += ` vk_button-width-tablet-${buttonWidthTablet}`;
491 }
492 if (buttonWidth) {
493 containerClass += ` vk_button-width-${buttonWidth}`;
494 }
495 } else {
496 containerClass += ` vk_button-align-${buttonAlign}`;
497 }
498
499 // エフェクト
500 if (buttonEffect !== '') {
501 containerClass += ` is-style-${buttonEffect}`;
502 }
503
504 // アイコン単位
505 const units = [
506 { value: 'px', label: 'px', default: 16 },
507 { value: 'em', label: 'em', default: 1 },
508 { value: 'rem', label: 'rem', default: 1 },
509 ];
510
511 const blockProps = useBlockProps({
512 className: containerClass,
513 });
514
515 // コア spacing.padding は wrapper (.vk_button) にインライン style として
516 // 自動注�
517 �されるが、編集画面でも save と同様に wrapper 側を取り除き、
518 // 同じ値を <a> 側に転写する。これによりエディタ表示と保存後 HTML を一致させる。
519 // Same wrapper-padding strip / forward to <a> as in save.js, so the editor
520 // preview matches the saved HTML.
521 const spacingPadding = attributes?.style?.spacing?.padding;
522 const hasSpacingPadding =
523 spacingPadding &&
524 (spacingPadding.top ||
525 spacingPadding.right ||
526 spacingPadding.bottom ||
527 spacingPadding.left);
528
529 if (hasSpacingPadding && blockProps.style) {
530 const {
531 paddingTop: _pt,
532 paddingRight: _pr,
533 paddingBottom: _pb,
534 paddingLeft: _pl,
535 ...restWrapperStyle
536 } = blockProps.style;
537 blockProps.style = restWrapperStyle;
538 }
539
540 let inlineStyle = {};
541 if (
542 buttonTextColorCustom !== undefined &&
543 isHexColor(buttonTextColorCustom)
544 ) {
545 inlineStyle = {
546 // 編集画面対策
547 color: `${buttonTextColorCustom}`,
548 };
549 }
550 if (hasSpacingPadding) {
551 if (spacingPadding.top) {
552 inlineStyle.paddingTop = toPresetSpacingVar(spacingPadding.top);
553 }
554 if (spacingPadding.right) {
555 inlineStyle.paddingRight = toPresetSpacingVar(spacingPadding.right);
556 }
557 if (spacingPadding.bottom) {
558 inlineStyle.paddingBottom = toPresetSpacingVar(
559 spacingPadding.bottom
560 );
561 }
562 if (spacingPadding.left) {
563 inlineStyle.paddingLeft = toPresetSpacingVar(spacingPadding.left);
564 }
565 }
566
567 // buttonTargetをlink-toolbarのlinkTargetに変換
568 const linkTarget = buttonTarget ? '_blank' : '';
569
570 return (
571 <>
572 <BlockControls>
573 <ToolbarGroup>
574 <LinkToolbar
575 linkUrl={buttonUrl || ''}
576 setLinkUrl={(value) =>
577 setAttributes({ buttonUrl: value })
578 }
579 linkTarget={linkTarget}
580 setLinkTarget={(value) =>
581 setAttributes({ buttonTarget: value === '_blank' })
582 }
583 relAttribute={relAttribute || ''}
584 setRelAttribute={(value) =>
585 setAttributes({ relAttribute: value })
586 }
587 isDescendentOfQueryLoop={isDescendentOfQueryLoop}
588 linkToPost={linkToPost}
589 setLinkToPost={(checked) =>
590 setAttributes({ linkToPost: !!checked })
591 }
592 linkToCustomField={linkToCustomField}
593 setLinkToCustomField={(checked) =>
594 setAttributes({
595 linkToCustomField: !!checked,
596 })
597 }
598 customFieldName={linkCustomFieldName}
599 setCustomFieldName={(value) =>
600 setAttributes({ linkCustomFieldName: value })
601 }
602 />
603 </ToolbarGroup>
604 </BlockControls>
605 <InspectorControls>
606 <PanelBody title={__('Button setting', 'vk-blocks')}>
607 <TextControl
608 label={__('Sub Caption', 'vk-blocks')}
609 value={subCaption}
610 className={`mt-0 mb-3`}
611 onChange={(value) =>
612 setAttributes({ subCaption: value })
613 }
614 placeholder={__('Sub Caption', 'vk-blocks')}
615 />
616
617 {/* 見出しとカスタムサイズ切替トグルを 1 行に並べる。
618 コアの FontSizePicker のヘッダー(ラベル+歯車トグル)に倣う。
619 Lay out the heading and the custom-size toggle on one row,
620 mirroring the core FontSizePicker header (label + gear toggle). */}
621 <div className="vk-button-font-size-header mt-0 mb-2">
622 <h4 id={buttonSizeLabelId}>
623 {__('Button Size:', 'vk-blocks')}
624 </h4>
625 {/* プリセット⇔カスタムサイズを切り替えるトグル。
626 isPressed はコアの Button �
627 部で aria-pressed に変換されるため、
628 これだけでカスタムサイズモードの ON/OFF を支援技術へ伝えられる。
629 Toggle between preset and custom size. The core Button converts
630 isPressed into aria-pressed, so this alone conveys whether the
631 custom-size mode is on to assistive technologies. */}
632 <Button
633 className="vk-button-font-size-toggle"
634 size="small"
635 icon={settings}
636 isPressed={isCustomSize}
637 label={
638 isCustomSize
639 ? __('Use size preset', 'vk-blocks')
640 : __('Set custom size', 'vk-blocks')
641 }
642 showTooltip
643 onClick={() => {
644 if (isCustomSize) {
645 // カスタム → プリセットへ戻す。
646 // 数値指定を解除し、プリセットへ戻した旨を通知する。
647 // Custom -> preset. Clear the numeric size and
648 // announce the return to the preset.
649 setIsCustomSize(false);
650 if (fontSizeValue) {
651 setAttributes({ fontSizeValue: null });
652 setFontSizeAnnouncement(
653 __(
654 'Returned to the preset size.',
655 'vk-blocks'
656 )
657 );
658 }
659 } else {
660 // プリセット → カスタムへ。
661 // ここでは fontSizeValue を自動セットしない。ユーザーが
662 // 数値を�
663 �れるまで出力はプリセットのまま(dirty 化を避ける)。
664 // Preset -> custom. Do not auto-set fontSizeValue here;
665 // the output stays on the preset until the user enters a
666 // value (avoids marking the block dirty).
667 setIsCustomSize(true);
668 }
669 }}
670 />
671 </div>
672 {/* プリセットモード: XS/S/M/L/XL のセグメント。
673 カスタムサイズモード時は隠す(コア FontSizePicker と同じ挙動)。
674 Preset mode: the XS/S/M/L/XL segments. Hidden while in custom-size
675 mode (matching the core FontSizePicker behavior). */}
676 {!isCustomSize && (
677 <ToggleGroupControl
678 value={buttonSize}
679 // プリセットを選び直したら数値指定を解除してプリセットに戻す。
680 // Choosing a preset clears the numeric size and returns to the preset.
681 onChange={(value) => {
682 setAttributes({
683 buttonSize: value,
684 fontSizeValue: null,
685 });
686 if (fontSizeValue) {
687 setFontSizeAnnouncement(
688 __(
689 'Returned to the preset size.',
690 'vk-blocks'
691 )
692 );
693 }
694 }}
695 aria-labelledby={buttonSizeLabelId}
696 isBlock
697 >
698 <ToggleGroupControlOption
699 value="xs"
700 label={__('XS', 'vk-blocks')}
701 aria-label={__('Extra Small', 'vk-blocks')}
702 showTooltip
703 />
704 <ToggleGroupControlOption
705 value="sm"
706 label={__('S', 'vk-blocks')}
707 aria-label={__('Small', 'vk-blocks')}
708 showTooltip
709 />
710 <ToggleGroupControlOption
711 value="md"
712 label={__('M', 'vk-blocks')}
713 aria-label={__('Medium', 'vk-blocks')}
714 showTooltip
715 />
716 <ToggleGroupControlOption
717 value="lg"
718 label={__('L', 'vk-blocks')}
719 aria-label={__('Large', 'vk-blocks')}
720 showTooltip
721 />
722 <ToggleGroupControlOption
723 value="xl"
724 label={__('XL', 'vk-blocks')}
725 aria-label={__('Extra Large', 'vk-blocks')}
726 showTooltip
727 />
728 </ToggleGroupControl>
729 )}
730 {/* カスタムサイズモード: 数値�
731 �力(px固定)+スライダー。
732 コアの FontSizePicker(withSlider)の見た目を、min/max/step を
733 指定できる UnitControl + RangeControl の併置で再現する。コアの
734 FontSizePicker コンポーネント自体は min/max/step を props で
735 受け取れず、植草指定の min=10/max=100/step=1・px 固定を満たせない
736 ため採用しない(A-2 方針)。
737 Custom-size mode: a px-only numeric input plus a slider.
738 This reproduces the look of the core FontSizePicker (withSlider)
739 using a UnitControl + RangeControl placed side by side, because the
740 core FontSizePicker component cannot accept min/max/step props and so
741 cannot satisfy the specified min=10/max=100/step=1 px-only range. */}
742 {isCustomSize && (
743 <div className="vk-button-font-size-control mt-0 mb-2">
744 <div className="vk-button-font-size-control__row">
745 <UnitControl
746 // 保存値とクランプ確定値が一致して再レンダリングが
747 // 起きないケースで、blur 時にこのキーを変えて再マウントし
748 // 表示を保存値へ強制同期する(詳細は宣言箇所のコメント参�
749 �)。
750 // Bumping this key on blur remounts the control so its
751 // display snaps to the stored value when the clamped result
752 // matches the stored value and no re-render would occur.
753 key={fontSizeInputKey}
754 className="vk-button-font-size-control__unit"
755 label={__('Font Size', 'vk-blocks')}
756 value={fontSizeValue}
757 // 単位はコア準拠(px / em / rem / vw / vh)。レガシー値は
758 // ユーザーが能動的に触るまで温存し、勝手に変換しない。
759 // Units follow the core set (px / em / rem / vw / vh). Legacy
760 // values are kept until the user actively edits them; they are
761 // never auto-converted.
762 units={fontSizeUnits}
763 // 新規�
764 �力レンジ(下限10・上限100・px 整数)の�
765 保は
766 // handleUnitControlFontSize 側で明示的に行う。
767 // min/max を UnitControl に渡すと、レンジ外のレガシー値
768 // ('200px'・'1.2em' 等)をフォーカス→ブラーしただけで
769 // 受動的に境界値へクランプ&保存してしまい後方互換を壊すため、
770 // あえて min/max は渡さない(step は整数�
771 �力のUIヒント)。
772 // The new-input range (min 10, max 100, integer px) is enforced
773 // in handleUnitControlFontSize. min/max are intentionally NOT
774 // passed to UnitControl because they would passively clamp and
775 // persist out-of-range legacy values ('200px', '1.2em') on mere
776 // focus/blur, breaking backward compatibility. (step is only a
777 // UI hint for integer input.)
778 step={1}
779 // 数値指定したらプリセットより優�
780 �。空ならプリセットへ戻す。
781 // Setting a value makes it take priority over the preset; an
782 // empty value returns control to the preset.
783 onChange={(value) =>
784 handleUnitControlFontSize(value)
785 }
786 // 編集確定(blur)時に、表示中のドラフトが保存確定値と
787 // 乖離していれば再マウントして表示を揃える。
788 // On blur, remount to align the display when the draft
789 // diverges from the committed stored value.
790 onBlur={handleUnitControlFontSizeBlur}
791 />
792 {/* スライダー。数値�
793 �力は UnitControl 側に集約するため、
794 ここでは�
795 �力欄を出さない(spinbutton の二重化を防ぐ)。
796 見出しラベルを持たないので aria-label で操作対象を明示する。
797 The slider. The numeric input lives in the UnitControl, so
798 this hides its own input field to avoid duplicate spinbuttons.
799 It has no visible label, so aria-label states its purpose. */}
800 <RangeControl
801 className="vk-button-font-size-control__slider"
802 aria-label={__('Font Size', 'vk-blocks')}
803 value={sliderFontSizeValue}
804 min={sliderFontMin}
805 max={sliderFontMax}
806 step={sliderFontStep}
807 withInputField={false}
808 showTooltip
809 // スライダー操作時は常に px の数値として確定させる。
810 // On slider change, always commit a px numeric value.
811 onChange={(value) =>
812 updateCustomFontSize(
813 value === undefined ||
814 value === null
815 ? null
816 : `${value}${currentFontUnit}`
817 )
818 }
819 />
820 </div>
821 </div>
822 )}
823 {/* 状�
824 �変化をスクリーンリーダーへ通知する live region。
825 Live region announcing the state change to screen readers. */}
826 <div
827 className="screen-reader-text"
828 aria-live="polite"
829 aria-atomic="true"
830 >
831 {fontSizeAnnouncement}
832 </div>
833 <style>
834 {`
835 /* 見出しと歯車トグルを両端に�
836 �置する。
837 Place the heading and the gear toggle at opposite ends. */
838 .vk-button-font-size-header {
839 display: flex;
840 align-items: center;
841 justify-content: space-between;
842 gap: 8px;
843 }
844 /* ヘッダー�
845 の見出しは flex で縦中央に揃えるためマージンを消す。
846 Drop the heading margin so flex can center it vertically. */
847 .vk-button-font-size-header h4 {
848 margin: 0;
849 }
850 /* カスタムサイズUI�
851 �体のラッパー。数値�
852 �力とスライダーを __row �
853
854 横1行で収める(コア FontSizePicker と同じ構成)。
855 Wrapper for the whole custom-size UI. The numeric input and slider
856 sit on a single __row (same layout as the core FontSizePicker). */
857 .vk-button-font-size-control {
858 display: flex;
859 flex-direction: column;
860 align-items: stretch;
861 }
862 /* 数値�
863 �力(UnitControl)とスライダー(RangeControl)を横1行に並べ、
864 コアの FontSizePicker(数値+px+スライダー)の見た目に寄せる。
865 狭いサイドバーでも縦積みにならないよう折り返しはさせず、
866 数値�
867 �力はコンパクトな固定�
868 、スライダーが残り�
869 を埋める。
870 Lay out the numeric input (UnitControl) and the slider
871 (RangeControl) on a single row to mirror the core FontSizePicker
872 (number + px + slider). Do not wrap (so they never stack on a
873 narrow sidebar): the numeric input keeps a compact fixed width
874 and the slider fills the remaining space. */
875 .vk-button-font-size-control__row {
876 display: flex;
877 align-items: center;
878 gap: 8px;
879 }
880 /* 数値�
881 �力はコンパクトな固定�
882 。スライダーは残り�
883 いっぱい。
884 min-width:0 で flex 子要素が�
885 容�
886 を�
887 えて縮められるようにする。
888 The numeric input takes a compact fixed width; the slider fills
889 the rest. min-width:0 lets the flex children shrink below their
890 content width. */
891 .vk-button-font-size-control__unit {
892 flex: 0 1 104px;
893 min-width: 0;
894 }
895 .vk-button-font-size-control__slider {
896 flex: 1 1 auto;
897 min-width: 0;
898 margin: 0;
899 }
900 `}
901 </style>
902 {!isInnerButton && (
903 <>
904 <h4 className="mt-0 mb-2">
905 {__('Button Position:', 'vk-blocks')}
906 </h4>
907 <ToggleGroupControl
908 value={buttonAlign}
909 onChange={(value) =>
910 setAttributes({ buttonAlign: value })
911 }
912 className="vk-button-align-control"
913 isBlock
914 >
915 <ToggleGroupControlOption
916 value="left"
917 label={__('Left', 'vk-blocks')}
918 />
919 <ToggleGroupControlOption
920 value="center"
921 label={__('Center', 'vk-blocks')}
922 />
923 <ToggleGroupControlOption
924 value="right"
925 label={__('Right', 'vk-blocks')}
926 />
927 <ToggleGroupControlOption
928 value="wide"
929 label={__('Wide', 'vk-blocks')}
930 />
931 <ToggleGroupControlOption
932 value="block"
933 label={__('Block', 'vk-blocks')}
934 />
935 </ToggleGroupControl>
936 <style>
937 {`
938 .vk-button-align-control .components-toggle-group-control-option-base {
939 padding: 0;
940 }
941 `}
942 </style>
943 </>
944 )}
945
946 {isInnerButton && (
947 <>
948 <h4 className="mt-0 mb-2">
949 {__('Button Width:', 'vk-blocks')}
950 </h4>
951 <p className="mt-0 mb-2">
952 {__('Mobile', 'vk-blocks')}
953 </p>
954 <ToggleGroupControl
955 value={String(buttonWidthMobile)}
956 onChange={(value) => {
957 setAttributes({
958 buttonWidthMobile: Number(value),
959 });
960 }}
961 isBlock
962 >
963 <ToggleGroupControlOption
964 value="0"
965 label="Auto"
966 />
967 <ToggleGroupControlOption
968 value="25"
969 label="25%"
970 />
971 <ToggleGroupControlOption
972 value="50"
973 label="50%"
974 />
975 <ToggleGroupControlOption
976 value="75"
977 label="75%"
978 />
979 <ToggleGroupControlOption
980 value="100"
981 label="100%"
982 />
983 </ToggleGroupControl>
984 <p className="mt-0 mb-2">
985 {__('Tablet', 'vk-blocks')}
986 </p>
987
988 <ToggleGroupControl
989 value={String(buttonWidthTablet)}
990 onChange={(value) => {
991 setAttributes({
992 buttonWidthTablet: Number(value),
993 });
994 }}
995 isBlock
996 >
997 <ToggleGroupControlOption
998 value="0"
999 label="Auto"
1000 />
1001 <ToggleGroupControlOption
1002 value="25"
1003 label="25%"
1004 />
1005 <ToggleGroupControlOption
1006 value="50"
1007 label="50%"
1008 />
1009 <ToggleGroupControlOption
1010 value="75"
1011 label="75%"
1012 />
1013 <ToggleGroupControlOption
1014 value="100"
1015 label="100%"
1016 />
1017 </ToggleGroupControl>
1018
1019 <p className="mt-0 mb-2">{__('PC', 'vk-blocks')}</p>
1020 <ToggleGroupControl
1021 value={String(buttonWidth)}
1022 onChange={(value) => {
1023 setAttributes({
1024 buttonWidth: Number(value),
1025 });
1026 }}
1027 isBlock
1028 >
1029 <ToggleGroupControlOption
1030 value="0"
1031 label="Auto"
1032 />
1033 <ToggleGroupControlOption
1034 value="25"
1035 label="25%"
1036 />
1037 <ToggleGroupControlOption
1038 value="50"
1039 label="50%"
1040 />
1041 <ToggleGroupControlOption
1042 value="75"
1043 label="75%"
1044 />
1045 <ToggleGroupControlOption
1046 value="100"
1047 label="100%"
1048 />
1049 </ToggleGroupControl>
1050 </>
1051 )}
1052
1053 <h4 className="mt-0 mb-2">
1054 {__('Button Style:', 'vk-blocks')}
1055 </h4>
1056 <ToggleGroupControl
1057 value={buttonType}
1058 onChange={(value) => {
1059 setAttributes({ buttonType: value });
1060
1061 if (value === '1' || value === '2') {
1062 setAttributes({
1063 buttonTextColorCustom: undefined,
1064 buttonEffect: '',
1065 });
1066 }
1067 if (value === '2') {
1068 setAttributes({
1069 buttonHoverBgColorCustom: undefined,
1070 });
1071 }
1072 }}
1073 isBlock
1074 >
1075 <ToggleGroupControlOption
1076 value="0"
1077 label={__('Solid color', 'vk-blocks')}
1078 />
1079 <ToggleGroupControlOption
1080 value="1"
1081 label={__('No background', 'vk-blocks')}
1082 />
1083 <ToggleGroupControlOption
1084 value="2"
1085 label={__('Text only', 'vk-blocks')}
1086 />
1087 </ToggleGroupControl>
1088 <p className={`mb-3`}>
1089 {__(
1090 'If you select "No background", you need to select a custom color.',
1091 'vk-blocks'
1092 )}
1093 </p>
1094
1095 {'0' === buttonType && (
1096 <>
1097 <h4 className="mt-0 mb-2">
1098 {__('Button Effect:', 'vk-blocks')}
1099 </h4>
1100 <ToggleGroupControl
1101 value={buttonEffect}
1102 onChange={(value) =>
1103 setAttributes({ buttonEffect: value })
1104 }
1105 isBlock
1106 >
1107 <ToggleGroupControlOption
1108 value="none"
1109 label={__('None', 'vk-blocks')}
1110 />
1111 <ToggleGroupControlOption
1112 value="shine"
1113 label={__('Shine', 'vk-blocks')}
1114 />
1115 </ToggleGroupControl>
1116 </>
1117 )}
1118
1119 <h4 className={`mt-0 mb-2`}>{__('Color', 'vk-blocks')}</h4>
1120 <SelectControl
1121 label={__('Default Color (Bootstrap)', 'vk-blocks')}
1122 value={buttonColor}
1123 options={[
1124 {
1125 label: __('Primary', 'vk-blocks'),
1126 value: 'primary',
1127 },
1128 {
1129 label: __('Secondary', 'vk-blocks'),
1130 value: 'secondary',
1131 },
1132 {
1133 label: __('Success', 'vk-blocks'),
1134 value: 'success',
1135 },
1136 {
1137 label: __('Info', 'vk-blocks'),
1138 value: 'info',
1139 },
1140 {
1141 label: __('Warning', 'vk-blocks'),
1142 value: 'warning',
1143 },
1144 {
1145 label: __('Danger', 'vk-blocks'),
1146 value: 'danger',
1147 },
1148 {
1149 label: __('Light', 'vk-blocks'),
1150 value: 'light',
1151 },
1152 {
1153 label: __('Dark', 'vk-blocks'),
1154 value: 'dark',
1155 },
1156 {
1157 label: __('Custom Color', 'vk-blocks'),
1158 value: 'custom',
1159 },
1160 ]}
1161 onChange={(value) =>
1162 setAttributes({ buttonColor: value })
1163 }
1164 />
1165 <BaseControl
1166 label={__('Custom Color', 'vk-blocks')}
1167 id={`vk_block_button_custom_color`}
1168 >
1169 <BaseControl
1170 id={`vk_block_button_custom_background_color`}
1171 label={
1172 buttonType === '0' || buttonType === null
1173 ? __('Background Color', 'vk-blocks')
1174 : __('Button Color', 'vk-blocks')
1175 }
1176 help={__(
1177 'This color palette overrides the default color. If you want to use the default color, click the clear button.',
1178 'vk-blocks'
1179 )}
1180 >
1181 <AdvancedColorPalette
1182 schema={'buttonColorCustom'}
1183 {...props}
1184 />
1185 </BaseControl>
1186 {(buttonType === '0' || buttonType === null) &&
1187 buttonColorCustom !== undefined && (
1188 <BaseControl
1189 id={`vk_block_button_custom_text_color`}
1190 label={__('Text Color', 'vk-blocks')}
1191 >
1192 <AdvancedColorPalette
1193 schema={'buttonTextColorCustom'}
1194 {...props}
1195 />
1196 </BaseControl>
1197 )}
1198 </BaseControl>
1199 <h4 className={`mt-0 mb-2`}>
1200 {__('Hover Color', 'vk-blocks')}
1201 </h4>
1202 <BaseControl id={`vk_block_button_hover_color`}>
1203 {buttonType !== '2' && (
1204 <BaseControl
1205 id={`vk_block_button_hover_background_color`}
1206 label={__(
1207 'Hover Background Color',
1208 'vk-blocks'
1209 )}
1210 >
1211 <AdvancedColorPalette
1212 schema={'buttonHoverBgColorCustom'}
1213 {...props}
1214 />
1215 </BaseControl>
1216 )}
1217 <BaseControl
1218 id={`vk_block_button_hover_text_color`}
1219 label={__('Hover Text Color', 'vk-blocks')}
1220 >
1221 <AdvancedColorPalette
1222 schema={'buttonHoverTextColorCustom'}
1223 {...props}
1224 />
1225 </BaseControl>
1226 </BaseControl>
1227 <BaseControl>
1228 <h4 className={`mt-0 mb-2`}>
1229 {iconLabel(__('Icon', 'vk-blocks'))}
1230 </h4>
1231 <BaseControl
1232 id={`vk_block_button_fa_before_text`}
1233 label={__('Before text', 'vk-blocks')}
1234 >
1235 <FontAwesome
1236 attributeName={'fontAwesomeIconBefore'}
1237 {...props}
1238 />
1239 <UnitControl
1240 label={__('Size', 'vk-blocks')}
1241 value={iconSizeBefore}
1242 units={units}
1243 onChange={(value) => {
1244 setAttributes({
1245 iconSizeBefore: parseFloat(value)
1246 ? value
1247 : null,
1248 });
1249 }}
1250 />
1251 </BaseControl>
1252 <hr />
1253 <BaseControl
1254 id={`vk_block_button_fa_after_text`}
1255 label={__('After text', 'vk-blocks')}
1256 >
1257 <FontAwesome
1258 attributeName={'fontAwesomeIconAfter'}
1259 {...props}
1260 />
1261 <UnitControl
1262 label={__('Size', 'vk-blocks')}
1263 value={iconSizeAfter}
1264 units={units}
1265 onChange={(value) => {
1266 setAttributes({
1267 iconSizeAfter: parseFloat(value)
1268 ? value
1269 : null,
1270 });
1271 }}
1272 />
1273 </BaseControl>
1274 </BaseControl>
1275 <h4 className={`mt-0 mb-2`}>
1276 {__('Button border radius', 'vk-blocks')}
1277 </h4>
1278 <UnitControl
1279 value={attributes.borderRadius}
1280 onChange={(value) => {
1281 setAttributes({ borderRadius: value || null });
1282 }}
1283 units={[
1284 { value: 'px', label: 'px', default: 5 },
1285 { value: '%', label: '%', default: 5 },
1286 { value: 'em', label: 'em', default: 1 },
1287 { value: 'rem', label: 'rem', default: 1 },
1288 ]}
1289 />
1290 </PanelBody>
1291 </InspectorControls>
1292 <div {...blockProps}>
1293 <VKBButton
1294 lbTextColorCustom={buttonTextColorCustom}
1295 lbColorCustom={buttonColorCustom}
1296 lbColor={buttonColor}
1297 lbType={buttonType}
1298 lbAlign={buttonAlign}
1299 lbSize={buttonSize}
1300 lbFontAwesomeIconBefore={fontAwesomeIconBefore}
1301 lbFontAwesomeIconAfter={fontAwesomeIconAfter}
1302 lbIconSizeBefore={iconSizeBefore}
1303 lbIconSizeAfter={iconSizeAfter}
1304 lbsubCaption={subCaption}
1305 lbFontSizeValue={fontSizeValue}
1306 lbsubCaptionRichText={
1307 isSelected || subCaption ? (
1308 <RichText
1309 tagName="span"
1310 value={subCaption}
1311 onChange={(value) =>
1312 setAttributes({ subCaption: value })
1313 }
1314 placeholder={__('Sub Caption', 'vk-blocks')}
1315 allowedFormats={[]}
1316 aria-label={__('Sub Caption', 'vk-blocks')}
1317 />
1318 ) : null
1319 }
1320 inlineStyle={{
1321 ...inlineStyle,
1322 borderRadius: attributes.borderRadius,
1323 }}
1324 lbRichtext={
1325 <RichText
1326 tagName={'span'}
1327 className={'vk_button_link_txt'}
1328 onChange={(value) =>
1329 setAttributes({ content: value })
1330 }
1331 value={content}
1332 placeholder={__('Input text', 'vk-blocks')}
1333 allowedFormats={[
1334 'core/bold',
1335 // 'core/code',
1336 // 'core/image',
1337 'core/italic',
1338 // 'core/link',
1339 'core/strikethrough',
1340 // 'core/underline',
1341 // 'core/text-color',
1342 'core/superscript',
1343 'core/subscript',
1344 // 'vk-blocks/highlighter',
1345 'vk-blocks/responsive-br',
1346 'vk-blocks/nowrap',
1347 'vk-blocks/inline-font-size',
1348 ]}
1349 />
1350 }
1351 />
1352 </div>
1353 </>
1354 );
1355 }
1356