PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.7
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.7
2.0.13 2.0.12 2.0.11 2.0.10 2.0.9 trunk 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8
blockenberg / blocks / contrast-checker / index.js

index.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.7, at blocks/contrast-checker/index.js

176 lines 11.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 ( function () {
2 var el = wp.element.createElement;
3 var useState = wp.element.useState;
4 var Fragment = wp.element.Fragment;
5 var registerBlockType = wp.blocks.registerBlockType;
6 var __ = wp.i18n.__;
7 var InspectorControls = wp.blockEditor.InspectorControls;
8 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
9 var useBlockProps = wp.blockEditor.useBlockProps;
10 var PanelBody = wp.components.PanelBody;
11 var RangeControl = wp.components.RangeControl;
12 var TextControl = wp.components.TextControl;
13 var ToggleControl = wp.components.ToggleControl;
14
15 var _cchkTC, _cchkTV;
16 function _tc() { return _cchkTC || (_cchkTC = window.bkbgTypographyControl); }
17 function _tv(obj, prefix) { var fn = _cchkTV || (_cchkTV = window.bkbgTypoCssVars); return fn ? fn(obj, prefix) : {}; }
18
19 function hexToRgb(hex) {
20 hex = hex.replace('#','');
21 if (hex.length === 3) hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
22 return { r: parseInt(hex.slice(0,2),16), g: parseInt(hex.slice(2,4),16), b: parseInt(hex.slice(4,6),16) };
23 }
24 function luminance(hex) {
25 var rgb = hexToRgb(hex);
26 var vals = [rgb.r, rgb.g, rgb.b].map(function(v) {
27 v = v / 255;
28 return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
29 });
30 return 0.2126 * vals[0] + 0.7152 * vals[1] + 0.0722 * vals[2];
31 }
32 function contrastRatio(fg, bg) {
33 var l1 = luminance(fg), l2 = luminance(bg);
34 var lighter = Math.max(l1, l2), darker = Math.min(l1, l2);
35 return (lighter + 0.05) / (darker + 0.05);
36 }
37
38 var WCAG = [
39 { id: 'aa-normal', label: 'AA', sub: 'Normal text', min: 4.5 },
40 { id: 'aa-large', label: 'AA', sub: 'Large text', min: 3.0 },
41 { id: 'aaa-normal', label: 'AAA', sub: 'Normal text', min: 7.0 },
42 { id: 'aaa-large', label: 'AAA', sub: 'Large text', min: 4.5 }
43 ];
44
45 function RatioDisplay(props) {
46 var r = props.ratio;
47 var pass = r >= 4.5 ? 'Excellent' : r >= 3 ? 'Good' : r >= 2 ? 'Poor' : 'Fail';
48 var col = r >= 4.5 ? props.passColor : r >= 3 ? '#d97706' : props.failColor;
49 return el('div', { className: 'bkbg-cc-ratio-box', style: { borderColor: col + '44', background: col + '10' } },
50 el('div', { className: 'bkbg-cc-ratio-val', style: { color: col } }, r.toFixed(2) + ':1'),
51 el('div', { className: 'bkbg-cc-ratio-lbl', style: { color: props.labelColor } }, 'Contrast Ratio'),
52 el('div', { className: 'bkbg-cc-ratio-grade', style: { background: col, color: '#fff' } }, pass)
53 );
54 }
55
56 function WcagRow(props) {
57 var pass = props.ratio >= props.level.min;
58 var col = pass ? props.passColor : props.failColor;
59 return el('div', { className: 'bkbg-cc-wcag-row', style: { borderColor: col + '33', background: col + '09' } },
60 el('div', { className: 'bkbg-cc-wcag-badge', style: { background: col, color: '#fff' } }, props.level.label),
61 el('div', { className: 'bkbg-cc-wcag-info' },
62 el('div', { className: 'bkbg-cc-wcag-sub', style: { color: props.labelColor } }, props.level.sub),
63 el('div', { className: 'bkbg-cc-wcag-req', style: { color: '#9ca3af' } }, 'Min ' + props.level.min + ':1')
64 ),
65 el('div', { className: 'bkbg-cc-wcag-status', style: { color: col } },
66 pass ? '✓ Pass' : '✗ Fail'
67 )
68 );
69 }
70
71 function Editor(props) {
72 var a = props.attributes, sa = props.setAttributes;
73 var fg = useState(a.defaultFg || '#1e1b4b'); var fgVal = fg[0]; var setFg = fg[1];
74 var bg = useState(a.defaultBg || '#ffffff'); var bgVal = bg[0]; var setBg = bg[1];
75 var ratio = contrastRatio(fgVal, bgVal);
76 var lc = a.labelColor || '#374151';
77
78 var blockProps = useBlockProps({ className: 'bkbg-cc-app', style: Object.assign({}, _tv(a.typoTitle, '--bkcchk-title-'), _tv(a.typoSubtitle, '--bkcchk-sub-')) });
79
80 return el(Fragment, null,
81 el(InspectorControls, null,
82 el(PanelBody, { title: __('Content'), initialOpen: true },
83 el(TextControl, { label: __('Title'), value: a.title || '', onChange: function(v){ sa({ title: v }); } }),
84 el(TextControl, { label: __('Subtitle'), value: a.subtitle || '', onChange: function(v){ sa({ subtitle: v }); } }),
85 el(TextControl, { label: __('Preview Text'), value: a.previewText || '', onChange: function(v){ sa({ previewText: v }); } }),
86 el(TextControl, { label: __('Default Foreground Color'), value: a.defaultFg || '#1e1b4b', onChange: function(v){ sa({ defaultFg: v }); setFg(v); } }),
87 el(TextControl, { label: __('Default Background Color'), value: a.defaultBg || '#ffffff', onChange: function(v){ sa({ defaultBg: v }); setBg(v); } }),
88 el(ToggleControl, { __nextHasNoMarginBottom: true, label: __('Show Text Preview'), checked: a.showPreview !== false, onChange: function(v){ sa({ showPreview: v }); } }),
89 el(ToggleControl, { __nextHasNoMarginBottom: true, label: __('Show Color Suggestions'), checked: a.showSuggestions !== false, onChange: function(v){ sa({ showSuggestions: v }); } })
90 ),
91
92 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
93 _tc() && el(_tc(), { label: __('Title', 'blockenberg'), value: a.typoTitle, onChange: function(v){ sa({ typoTitle: v }); } }),
94 _tc() && el(_tc(), { label: __('Subtitle', 'blockenberg'), value: a.typoSubtitle, onChange: function(v){ sa({ typoSubtitle: v }); } }),
95 el(RangeControl, { label: __('Sample text font size (px)'), value: a.sampleFontSize || 16, min: 11, max: 28, onChange: function(v){ sa({ sampleFontSize: v }); }, __nextHasNoMarginBottom: true })
96 ),
97 el(PanelColorSettings, {
98 title: __('Colors'),
99 initialOpen: false,
100 colorSettings: [
101 { value: a.passColor ||'#16a34a', onChange: function(v){ sa({ passColor: v }); }, label: 'Pass Badge' },
102 { value: a.failColor ||'#dc2626', onChange: function(v){ sa({ failColor: v }); }, label: 'Fail Badge' },
103 { value: a.accentColor ||'#6c3fb5', onChange: function(v){ sa({ accentColor: v }); }, label: 'Accent' },
104 { value: a.cardBg ||'#ffffff', onChange: function(v){ sa({ cardBg: v }); }, label: 'Card Background' },
105 { value: a.sectionBg ||'#f5f3ff', onChange: function(v){ sa({ sectionBg: v }); }, label: 'Section Background' },
106 { value: a.titleColor ||'#111827', onChange: function(v){ sa({ titleColor: v }); }, label: 'Title' },
107 { value: a.subtitleColor ||'#6b7280', onChange: function(v){ sa({ subtitleColor: v }); }, label: 'Subtitle' },
108 { value: a.labelColor ||'#374151', onChange: function(v){ sa({ labelColor: v }); }, label: 'Labels' }
109 ]
110 }),
111 el(PanelBody, { title: __('Sizing'), initialOpen: false },
112 el(RangeControl, { label: __('Max Width (px)'), value: a.contentMaxWidth || 680, min: 360, max: 1400, step: 10, onChange: function(v){ sa({ contentMaxWidth: v }); } })
113 )
114 ),
115
116 el('div', blockProps,
117 el('div', { className: 'bkbg-cc-card', style: { background: a.cardBg||'#fff', maxWidth: (a.contentMaxWidth||680)+'px' } },
118 el('h2', { className: 'bkbg-cc-title', style: { color: a.titleColor||'#111827' } },
119 a.title || 'Color Contrast Checker'
120 ),
121 el('p', { className: 'bkbg-cc-subtitle', style: { color: a.subtitleColor||'#6b7280' } },
122 a.subtitle || 'Test accessibility compliance with WCAG 2.1 standards'
123 ),
124
125 // Color pickers
126 el('div', { className: 'bkbg-cc-pickers', style: { background: a.sectionBg||'#f5f3ff' } },
127 el('div', { className: 'bkbg-cc-picker-group' },
128 el('label', { className: 'bkbg-cc-label', style: { color: lc } }, '�
129 � Foreground Color'),
130 el('div', { className: 'bkbg-cc-color-row' },
131 el('input', { type: 'color', className: 'bkbg-cc-color-input', value: fgVal, onChange: function(e){ setFg(e.target.value); } }),
132 el('span', { className: 'bkbg-cc-hex', style: { color: lc } }, fgVal.toUpperCase())
133 )
134 ),
135 el('button', { className: 'bkbg-cc-swap', style: { background: a.accentColor||'#6c3fb5' }, onClick: function(){ var t=fgVal; setFg(bgVal); setBg(t); } }, ''),
136 el('div', { className: 'bkbg-cc-picker-group' },
137 el('label', { className: 'bkbg-cc-label', style: { color: lc } }, '◻ Background Color'),
138 el('div', { className: 'bkbg-cc-color-row' },
139 el('input', { type: 'color', className: 'bkbg-cc-color-input', value: bgVal, onChange: function(e){ setBg(e.target.value); } }),
140 el('span', { className: 'bkbg-cc-hex', style: { color: lc } }, bgVal.toUpperCase())
141 )
142 )
143 ),
144
145 // Preview
146 a.showPreview !== false && el('div', { className: 'bkbg-cc-preview', style: { background: bgVal } },
147 el('p', { className: 'bkbg-cc-preview-normal', style: { color: fgVal, fontSize: (a.sampleFontSize || 16) + 'px' } }, a.previewText || 'Normal text sample — 16px'),
148 el('p', { className: 'bkbg-cc-preview-large', style: { color: fgVal, fontSize: Math.round((a.sampleFontSize || 16) * 1.5) + 'px' } }, a.previewText || 'Large text sample — 24px'),
149 el('p', { className: 'bkbg-cc-preview-bold', style: { color: fgVal } }, a.previewText || 'Bold text — 14px bold')
150 ),
151
152 // Ratio display
153 el(RatioDisplay, { ratio: ratio, passColor: a.passColor||'#16a34a', failColor: a.failColor||'#dc2626', labelColor: lc }),
154
155 // WCAG levels
156 el('div', { className: 'bkbg-cc-wcag-grid' },
157 WCAG.map(function(level) {
158 return el(WcagRow, { key: level.id, ratio: ratio, level: level, passColor: a.passColor||'#16a34a', failColor: a.failColor||'#dc2626', labelColor: lc });
159 })
160 )
161 )
162 )
163 );
164 }
165
166 registerBlockType('blockenberg/contrast-checker', {
167 edit: Editor,
168 save: function(props) {
169 var a = props.attributes;
170 return el('div', useBlockProps.save(),
171 el('div', { className: 'bkbg-cc-app', 'data-opts': JSON.stringify(a) })
172 );
173 }
174 });
175 }() );
176