index.js
324 lines
| 1 | /** |
| 2 | * WordPress dependencies |
| 3 | */ |
| 4 | import { __, getLocaleData } from '@wordpress/i18n'; |
| 5 | import { addFilter } from '@wordpress/hooks'; |
| 6 | import { PanelBody, Button, ExternalLink } from '@wordpress/components'; |
| 7 | import { InspectorControls, transformStyles } from '@wordpress/block-editor'; |
| 8 | import { createHigherOrderComponent, useInstanceId } from '@wordpress/compose'; |
| 9 | import { hasBlockSupport } from '@wordpress/blocks'; |
| 10 | import { useEffect } from '@wordpress/element'; |
| 11 | import { select } from '@wordpress/data'; |
| 12 | /** |
| 13 | * External dependencies |
| 14 | */ |
| 15 | import classnames from 'classnames'; |
| 16 | |
| 17 | /** |
| 18 | * Internal dependencies |
| 19 | */ |
| 20 | import { CodeMirrorCss } from '@vkblocks/components/code-mirror-css'; |
| 21 | import { emptyStringToUndefined } from '@vkblocks/utils/empty-string-to-undefined'; |
| 22 | import { getSettingsPageUrl } from '@vkblocks/utils/settings-page-url'; |
| 23 | import { VkPanelIcon } from '@vkblocks/components/vk-icon'; |
| 24 | /*globals vk_blocks_params */ |
| 25 | |
| 26 | export const inString = (str, keyword) => { |
| 27 | return str.indexOf(keyword) !== -1; |
| 28 | }; |
| 29 | |
| 30 | export const hasCustomCssSupport = (blockName) => { |
| 31 | // 追加CSSクラスを許可していない場合はfalse |
| 32 | if (!hasBlockSupport(blockName, 'customClassName', true)) { |
| 33 | return false; |
| 34 | } |
| 35 | |
| 36 | const allowed = ['core', 'vk-blocks']; |
| 37 | let returnBool = |
| 38 | allowed.find((item) => inString(blockName, item)) !== undefined; |
| 39 | |
| 40 | const excludeBlocks = [ |
| 41 | // ExUnitに� |
| 42 | �っているvk blocksブロック |
| 43 | 'vk-blocks/share-button', |
| 44 | 'vk-blocks/child-page-index', |
| 45 | 'vk-blocks/contact-section', |
| 46 | 'vk-blocks/page-list-ancestor', |
| 47 | 'vk-blocks/sitemap', |
| 48 | 'vk-blocks/cta', |
| 49 | ]; |
| 50 | const excludeBlock = |
| 51 | excludeBlocks.find((excludeName) => |
| 52 | inString(blockName, excludeName) |
| 53 | ) !== undefined; |
| 54 | if (excludeBlock) { |
| 55 | returnBool = false; |
| 56 | } |
| 57 | return returnBool; |
| 58 | }; |
| 59 | |
| 60 | export const existsCss = (css) => { |
| 61 | // cssが存在するかつ空白文字のみではない |
| 62 | return css && css.match(/\S/g); |
| 63 | }; |
| 64 | |
| 65 | export const customCssSelectorRegex = /selector/g; |
| 66 | |
| 67 | /** |
| 68 | * Filters registered block settings. |
| 69 | * |
| 70 | * @param {Object} settings |
| 71 | */ |
| 72 | export function addAttribute(settings) { |
| 73 | if (!hasCustomCssSupport(settings.name)) { |
| 74 | return settings; |
| 75 | } |
| 76 | settings.attributes = { |
| 77 | ...settings.attributes, |
| 78 | vkbCustomCss: { |
| 79 | type: 'string', |
| 80 | }, |
| 81 | }; |
| 82 | return settings; |
| 83 | } |
| 84 | |
| 85 | /** |
| 86 | * Determine if the editor context is a widget screen |
| 87 | */ |
| 88 | const isWidgetsEditor = () => { |
| 89 | try { |
| 90 | // `core/edit-widgets` store が存在するか確認し、そこから� |
| 91 | 報を取得 |
| 92 | const widgetsStore = select('core/edit-widgets'); |
| 93 | return !!widgetsStore; |
| 94 | } catch (e) { |
| 95 | return false; |
| 96 | } |
| 97 | }; |
| 98 | |
| 99 | /** |
| 100 | * On the widget screen, only the blocks included in the allowList are true |
| 101 | * @param {string} blockName -Name of the block to check |
| 102 | */ |
| 103 | const shouldApplyOnThisScreenAndBlock = (blockName) => { |
| 104 | if (!isWidgetsEditor()) { |
| 105 | return false; |
| 106 | } |
| 107 | const allowList = new Set(['core/legacy-widget', 'core/widget-group']); |
| 108 | return allowList.has(blockName); |
| 109 | }; |
| 110 | |
| 111 | /** |
| 112 | * Override the default edit UI to include layout controls |
| 113 | */ |
| 114 | export const withInspectorControls = createHigherOrderComponent( |
| 115 | (BlockEdit) => (props) => { |
| 116 | const { name, attributes, setAttributes } = props; |
| 117 | if ( |
| 118 | !hasCustomCssSupport(name) || |
| 119 | shouldApplyOnThisScreenAndBlock(name) |
| 120 | ) { |
| 121 | return <BlockEdit {...props} />; |
| 122 | } |
| 123 | |
| 124 | const { vkbCustomCss, className } = attributes; |
| 125 | // 追加CSSを半角文字列で分けて� |
| 126 | �列化 |
| 127 | const nowClassArray = className ? className.split(' ') : []; |
| 128 | |
| 129 | // 追加 CSS クラスにvk_custom_cssがあるか |
| 130 | const existsCustomCssClass = (_nowClassArray) => { |
| 131 | return _nowClassArray.indexOf('vk_custom_css') !== -1 |
| 132 | ? true |
| 133 | : false; |
| 134 | }; |
| 135 | |
| 136 | // カスタムCSSにselectorがあるか |
| 137 | const existsCustomCssSelector = (_vkbCustomCss) => { |
| 138 | return customCssSelectorRegex.test(_vkbCustomCss); |
| 139 | }; |
| 140 | |
| 141 | // vkbCustomCssが変更されたとき |
| 142 | useEffect(() => { |
| 143 | if ( |
| 144 | !existsCustomCssClass(nowClassArray) && |
| 145 | existsCustomCssSelector(vkbCustomCss) |
| 146 | ) { |
| 147 | // カスタムCSS用クラスを追加 |
| 148 | setAttributes({ |
| 149 | className: emptyStringToUndefined( |
| 150 | classnames(nowClassArray, `vk_custom_css`) |
| 151 | ), |
| 152 | }); |
| 153 | } |
| 154 | |
| 155 | if ( |
| 156 | existsCustomCssClass(nowClassArray) && |
| 157 | !existsCustomCssSelector(vkbCustomCss) |
| 158 | ) { |
| 159 | // カスタムCSS用クラスを削除 |
| 160 | const deleteClass = nowClassArray.indexOf('vk_custom_css'); |
| 161 | nowClassArray.splice(deleteClass, 1); |
| 162 | setAttributes({ |
| 163 | className: emptyStringToUndefined( |
| 164 | classnames(nowClassArray) |
| 165 | ), |
| 166 | }); |
| 167 | } |
| 168 | }, [vkbCustomCss]); |
| 169 | |
| 170 | // classNameが変更されたときに |
| 171 | useEffect(() => { |
| 172 | if ( |
| 173 | !existsCustomCssClass(nowClassArray) && |
| 174 | existsCustomCssSelector(vkbCustomCss) |
| 175 | ) { |
| 176 | // カスタムCSS用クラスを追加 |
| 177 | setAttributes({ |
| 178 | className: emptyStringToUndefined( |
| 179 | classnames(`vk_custom_css`, nowClassArray) |
| 180 | ), |
| 181 | }); |
| 182 | } |
| 183 | }, [className]); |
| 184 | |
| 185 | return ( |
| 186 | <> |
| 187 | <BlockEdit {...props} /> |
| 188 | <InspectorControls> |
| 189 | <PanelBody |
| 190 | className={'vk_custom_css_panel'} |
| 191 | icon={ |
| 192 | <VkPanelIcon isActive={existsCss(vkbCustomCss)} /> |
| 193 | } |
| 194 | title={__('Custom CSS', 'vk-blocks')} |
| 195 | initialOpen={false} |
| 196 | > |
| 197 | <CodeMirrorCss |
| 198 | className="vk-codemirror-block-editor" |
| 199 | value={vkbCustomCss ? vkbCustomCss : ''} |
| 200 | onChange={(value) => { |
| 201 | setAttributes({ |
| 202 | vkbCustomCss: emptyStringToUndefined(value), |
| 203 | }); |
| 204 | }} |
| 205 | /> |
| 206 | <p> |
| 207 | {__( |
| 208 | 'If selector is specified, it is replaced by a block-specific CSS class. If selector is set to "selector", it will be replaced with a block-specific CSS class. CSS selectors other than "selector" may affect the entire page.', |
| 209 | 'vk-blocks' |
| 210 | )} |
| 211 | {(() => { |
| 212 | const lang = getLocaleData()[''].lang; |
| 213 | if (lang === 'ja_JP') { |
| 214 | return ( |
| 215 | <ExternalLink |
| 216 | href="https://www.vektor-inc.co.jp/service/wordpress-plugins/vk-blocks/vk-custom-css/" |
| 217 | target="_blank" |
| 218 | rel="noreferrer" |
| 219 | > |
| 220 | 詳しくはこちら |
| 221 | </ExternalLink> |
| 222 | ); |
| 223 | } |
| 224 | })()} |
| 225 | </p> |
| 226 | <p>{__('Example:', 'vk-blocks')}</p> |
| 227 | <pre className="vk-custom-css-sample-code"> |
| 228 | {'selector {\n background: #f5f5f5;\n}'} |
| 229 | </pre> |
| 230 | <p> |
| 231 | {__( |
| 232 | 'If you want the edit screen to be as close to the public screen as possible, or if your own CSS interferes with the CSS for the identification display and does not display as intended on the edit screen, please hide it.', |
| 233 | 'vk-blocks' |
| 234 | )} |
| 235 | </p> |
| 236 | <Button |
| 237 | href={getSettingsPageUrl('custom-css-setting')} |
| 238 | target="_blank" |
| 239 | rel="noreferrer" |
| 240 | variant="secondary" |
| 241 | isSmall |
| 242 | > |
| 243 | {__('Custom CSS Setting', 'vk-blocks')} |
| 244 | </Button> |
| 245 | </PanelBody> |
| 246 | </InspectorControls> |
| 247 | </> |
| 248 | ); |
| 249 | }, |
| 250 | 'withInspectorControls' |
| 251 | ); |
| 252 | |
| 253 | /** |
| 254 | * Override the default block element to include elements styles. |
| 255 | */ |
| 256 | const withElementsStyles = createHigherOrderComponent( |
| 257 | (BlockListBlock) => (props) => { |
| 258 | const { name, attributes } = props; |
| 259 | if ( |
| 260 | !hasCustomCssSupport(name) || |
| 261 | shouldApplyOnThisScreenAndBlock(name) |
| 262 | ) { |
| 263 | return <BlockListBlock {...props} />; |
| 264 | } |
| 265 | // 編集画面で使用出来る Unique id |
| 266 | // @see https://github.com/WordPress/gutenberg/blob/086b77ed409a70a6c6a6e74dee704851eff812f2/packages/compose/src/hooks/use-instance-id/README.md |
| 267 | const id = useInstanceId(BlockListBlock); |
| 268 | const uniqueClass = `vk_custom_css_${id}`; |
| 269 | |
| 270 | const { vkbCustomCss } = attributes; |
| 271 | // editor用のクラス名を追加 |
| 272 | const customCssClass = classnames(props.className, { |
| 273 | [uniqueClass]: existsCss(vkbCustomCss), |
| 274 | [`vk_edit_custom_css`]: |
| 275 | vk_blocks_params.show_custom_css_editor_flag === 'show' && |
| 276 | existsCss(vkbCustomCss), |
| 277 | }); |
| 278 | |
| 279 | // selectorをUniqueクラスに変換する |
| 280 | let cssTag = vkbCustomCss ? vkbCustomCss : ''; |
| 281 | if (cssTag && uniqueClass) { |
| 282 | cssTag = vkbCustomCss.replace( |
| 283 | customCssSelectorRegex, |
| 284 | '.' + uniqueClass |
| 285 | ); |
| 286 | } |
| 287 | |
| 288 | // cssに.editor-styles-wrapperをwrapする |
| 289 | if (cssTag !== '') { |
| 290 | cssTag = transformStyles( |
| 291 | [{ css: cssTag }], |
| 292 | '.editor-styles-wrapper' |
| 293 | ); |
| 294 | } |
| 295 | |
| 296 | return ( |
| 297 | <> |
| 298 | {(() => { |
| 299 | if (cssTag) { |
| 300 | return <style>{cssTag}</style>; |
| 301 | } |
| 302 | })()} |
| 303 | <BlockListBlock {...props} className={customCssClass} /> |
| 304 | </> |
| 305 | ); |
| 306 | } |
| 307 | ); |
| 308 | |
| 309 | addFilter( |
| 310 | 'blocks.registerBlockType', |
| 311 | 'vk-blocks/custom-css/addAttribute', |
| 312 | addAttribute |
| 313 | ); |
| 314 | addFilter( |
| 315 | 'editor.BlockEdit', |
| 316 | 'vk-blocks/editor/custom-css/with-inspector-controls', |
| 317 | withInspectorControls |
| 318 | ); |
| 319 | addFilter( |
| 320 | 'editor.BlockListBlock', |
| 321 | 'vk-blocks/style/with-block-controls', |
| 322 | withElementsStyles |
| 323 | ); |
| 324 |