PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.14
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.14
2.0.14 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 / time-zone-converter / index.js

index.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.14, at blocks/time-zone-converter/index.js

203 lines 14.1 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 _tc; function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
16 var _tv; function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
17 var _tvf = function (typo, prefix) { var fn = getTypoCssVars(); return fn ? fn(typo, prefix) : {}; };
18
19 var POPULAR_ZONES = [
20 'America/New_York','America/Chicago','America/Denver','America/Los_Angeles',
21 'America/Toronto','America/Mexico_City','America/Sao_Paulo','Europe/London',
22 'Europe/Paris','Europe/Berlin','Europe/Moscow','Africa/Lagos','Asia/Dubai',
23 'Asia/Kolkata','Asia/Shanghai','Asia/Tokyo','Asia/Singapore','Australia/Sydney',
24 'Pacific/Auckland','Pacific/Honolulu','UTC'
25 ];
26
27 function fmtZoneName(tz) {
28 return tz.replace(/_/g,' ').replace(/\//,' / ');
29 }
30
31 function getConvertedTime(isoString, fromZone, toZone, use12, showDate) {
32 try {
33 var opts = { timeZone: toZone, hour: '2-digit', minute: '2-digit', hour12: !!use12 };
34 if (showDate) {
35 opts.weekday = 'short';
36 opts.month = 'short';
37 opts.day = 'numeric';
38 }
39 var date = new Date(isoString);
40 return date.toLocaleString('en-US', opts);
41 } catch(e) { return '—'; }
42 }
43
44 function getOffset(isoString, toZone) {
45 try {
46 var date = new Date(isoString);
47 var utcMs = date.getTime();
48 var inZone = new Date(date.toLocaleString('en-US', {timeZone: toZone}));
49 var inUtc = new Date(date.toLocaleString('en-US', {timeZone: 'UTC'}));
50 var diff = Math.round((inZone - inUtc) / 60000);
51 var sign = diff >= 0 ? '+' : '-';
52 var abs = Math.abs(diff);
53 return 'UTC' + sign + Math.floor(abs/60) + (abs%60 ? ':' + String(abs%60).padStart(2,'0') : '');
54 } catch(e) { return ''; }
55 }
56
57 function TZPreview(props) {
58 var a = props.attributes;
59 var accent = a.accentColor || '#6c3fb5';
60
61 // Default to now (rounded to nearest minute)
62 var nowDef = (function(){
63 var d = new Date(); d.setSeconds(0,0);
64 // Build local datetime string for input[type=datetime-local]
65 var pad = function(n){ return String(n).padStart(2,'0'); };
66 return d.getFullYear()+'-'+pad(d.getMonth()+1)+'-'+pad(d.getDate())+'T'+pad(d.getHours())+':'+pad(d.getMinutes());
67 })();
68
69 var _dt = useState(nowDef); var dt = _dt[0]; var setDt = _dt[1];
70 var _from = useState(a.sourceZone||'America/New_York'); var fromZ = _from[0]; var setFromZ = _from[1];
71
72 var targets = String(a.targetZones||'Europe/London,Europe/Paris,Asia/Tokyo,America/Los_Angeles,Australia/Sydney')
73 .split(',').map(function(s){ return s.trim(); }).filter(Boolean);
74
75 // Convert dt (local datetime-local string) to an absolute ISO for Intl
76 var dateObj = new Date(dt);
77 var isoBase = isNaN(dateObj.getTime()) ? new Date().toISOString() : dateObj.toISOString();
78
79 // Display the source time in its own zone
80 var srcDisplay = getConvertedTime(isoBase, null, fromZ, a.use12Hour, a.showDate);
81 var srcOffset = getOffset(isoBase, fromZ);
82
83 return el('div', {style:{paddingTop:(a.paddingTop||60)+'px',paddingBottom:(a.paddingBottom||60)+'px',background:a.sectionBg||undefined}},
84 el('div', {style:{background:a.cardBg,borderRadius:(a.cardRadius||16)+'px',padding:'36px 32px',maxWidth:(a.maxWidth||540)+'px',margin:'0 auto',boxShadow:'0 4px 24px rgba(0,0,0,.09)'}},
85
86 (a.showTitle||a.showSubtitle) && el('div', {style:{marginBottom:'22px'}},
87 a.showTitle && el('div', {className:'bkbg-tzc-title',style:{color:a.titleColor,marginBottom:'6px'}}, a.title),
88 a.showSubtitle && el('div', {style:{fontSize:'15px',color:a.subtitleColor,opacity:.75}}, a.subtitle)
89 ),
90
91 // Source card
92 el('div', {style:{background:a.sourceBg||accent,borderRadius:(a.rowRadius||10)+'px',padding:'20px 22px',marginBottom:'16px'}},
93 el('div', {style:{display:'flex',gap:'12px',flexWrap:'wrap',marginBottom:'12px'}},
94 el('div', {style:{flex:1,minWidth:'180px'}},
95 el('label', {style:{display:'block',fontSize:'11px',fontWeight:700,color:a.sourceColor||'#fff',opacity:.8,marginBottom:'5px',textTransform:'uppercase',letterSpacing:'.05em'}}, 'Date & Time'),
96 el('input', {type:'datetime-local',value:dt,
97 style:{width:'100%',boxSizing:'border-box',padding:'9px 12px',borderRadius:'6px',border:'none',fontSize:'14px',fontFamily:'inherit',outline:'none',background:'rgba(255,255,255,.15)',color:a.sourceColor||'#fff',colorScheme:'dark'},
98 onChange:function(e){setDt(e.target.value);}})
99 ),
100 el('div', {style:{flex:1,minWidth:'180px'}},
101 el('label', {style:{display:'block',fontSize:'11px',fontWeight:700,color:a.sourceColor||'#fff',opacity:.8,marginBottom:'5px',textTransform:'uppercase',letterSpacing:'.05em'}}, 'Source Time Zone'),
102 el('select', {value:fromZ,
103 style:{width:'100%',boxSizing:'border-box',padding:'9px 36px 9px 12px',borderRadius:'6px',border:'none',fontSize:'14px',fontFamily:'inherit',outline:'none',background:'rgba(255,255,255,.15)',color:a.sourceColor||'#fff',cursor:'pointer',appearance:'none',WebkitAppearance:'none',backgroundImage:"url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23ffffff' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\")",backgroundRepeat:'no-repeat',backgroundPosition:'right 12px center',backgroundSize:'10px 6px'},
104 onChange:function(e){setFromZ(e.target.value);}},
105 POPULAR_ZONES.map(function(z){return el('option',{key:z,value:z},fmtZoneName(z));}))
106 )
107 ),
108 el('div', {className:'bkbg-tzc-source-time',style:{color:a.sourceColor||'#fff'}}, srcDisplay),
109 a.showOffset && el('div', {style:{fontSize:'13px',color:a.sourceColor||'#fff',opacity:.7,marginTop:'4px'}}, fmtZoneName(fromZ) + ' · ' + srcOffset)
110 ),
111
112 // Target rows
113 el('div', {style:{display:'flex',flexDirection:'column',gap:'8px'}},
114 targets.map(function(tz) {
115 var converted = getConvertedTime(isoBase, null, tz, a.use12Hour, a.showDate);
116 var offset = getOffset(isoBase, tz);
117 return el('div', {key:tz, style:{display:'flex',alignItems:'center',justifyContent:'space-between',background:a.rowBg||'#f9fafb',border:'1.5px solid '+(a.rowBorder||'#e5e7eb'),borderRadius:(a.rowRadius||10)+'px',padding:'14px 18px',gap:'12px'}},
118 el('div', null,
119 el('div', {style:{fontSize:'13px',fontWeight:600,color:a.rowZoneColor||'#6b7280',marginBottom:'2px'}}, fmtZoneName(tz)),
120 a.showOffset && el('div', {style:{fontSize:'12px',color:a.rowZoneColor||'#9ca3af',opacity:.8}}, offset)
121 ),
122 el('div', {className:'bkbg-tzc-row-time',style:{color:a.rowTimeColor||'#1f2937'}}, converted)
123 );
124 })
125 )
126 )
127 );
128 }
129
130 registerBlockType('blockenberg/time-zone-converter', {
131 edit: function(props) {
132 var a = props.attributes; var set = props.setAttributes;
133 var blockProps = useBlockProps((function () {
134 var tv = Object.assign({}, _tvf(a.titleTypo, '--bktzc-tt-'), _tvf(a.timeTypo, '--bktzc-tm-'));
135 return { style: tv };
136 })());
137 var colorSettings = [
138 { value: a.accentColor, onChange: function(v){set({accentColor:v});}, label: 'Accent Color' },
139 { value: a.cardBg, onChange: function(v){set({cardBg:v});}, label: 'Card Background' },
140 { value: a.sourceBg, onChange: function(v){set({sourceBg:v});}, label: 'Source Card Background' },
141 { value: a.sourceColor, onChange: function(v){set({sourceColor:v});}, label: 'Source Card Text' },
142 { value: a.rowBg, onChange: function(v){set({rowBg:v});}, label: 'Row Background' },
143 { value: a.rowBorder, onChange: function(v){set({rowBorder:v});}, label: 'Row Border' },
144 { value: a.rowTimeColor, onChange: function(v){set({rowTimeColor:v});}, label: 'Row Time Color' },
145 { value: a.rowZoneColor, onChange: function(v){set({rowZoneColor:v});}, label: 'Row Zone Label Color' },
146 { value: a.labelColor, onChange: function(v){set({labelColor:v});}, label: 'Label Color' },
147 { value: a.inputBorder, onChange: function(v){set({inputBorder:v});}, label: 'Input Border' },
148 { value: a.titleColor, onChange: function(v){set({titleColor:v});}, label: 'Title Color' },
149 { value: a.subtitleColor, onChange: function(v){set({subtitleColor:v});}, label: 'Subtitle Color' },
150 { value: a.sectionBg, onChange: function(v){set({sectionBg:v});}, label: 'Section Background' }
151 ];
152 return el(Fragment, null,
153 el(InspectorControls, null,
154 el(PanelBody, {title:'Header', initialOpen:false},
155 el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Show Title', checked:a.showTitle, onChange:function(v){set({showTitle:v});}}),
156 el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Show Subtitle',checked:a.showSubtitle,onChange:function(v){set({showSubtitle:v});}}),
157 el(TextControl,{label:'Title', value:a.title, onChange:function(v){set({title:v});}}),
158 el(TextControl,{label:'Subtitle',value:a.subtitle,onChange:function(v){set({subtitle:v});}})
159 ),
160 el(PanelBody, {title:'Time Zone Settings', initialOpen:true},
161 el(TextControl, {label:'Source Time Zone (IANA)', value:a.sourceZone, onChange:function(v){set({sourceZone:v});}}),
162 el(TextControl, {label:'Target Zones (comma-separated IANA)', value:a.targetZones, onChange:function(v){set({targetZones:v});},
163 help:'e.g. Europe/London,Asia/Tokyo,America/Los_Angeles'}),
164 el(ToggleControl,{__nextHasNoMarginBottom:true,label:'12-Hour Format', checked:a.use12Hour, onChange:function(v){set({use12Hour:v});}}),
165 el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Show Date', checked:a.showDate, onChange:function(v){set({showDate:v});}}),
166 el(ToggleControl,{__nextHasNoMarginBottom:true,label:'Show UTC Offset', checked:a.showOffset,onChange:function(v){set({showOffset:v});}})
167 ),
168
169 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
170 getTypoControl() && el(getTypoControl(), {
171 label: __('Title Typography', 'blockenberg'),
172 value: a.titleTypo || {},
173 onChange: function (v) { set({ titleTypo: v }); }
174 }),
175 getTypoControl() && el(getTypoControl(), {
176 label: __('Time Typography', 'blockenberg'),
177 value: a.timeTypo || {},
178 onChange: function (v) { set({ timeTypo: v }); }
179 })
180 ),
181 el(PanelColorSettings,{title:'Colors',initialOpen:false,colorSettings:colorSettings}),
182 el(PanelBody,{title:'Sizing & Layout',initialOpen:false},
183 el(RangeControl,{label:'Card Border Radius', value:a.cardRadius, min:0, max:40, step:1, onChange:function(v){set({cardRadius:v});}}),
184 el(RangeControl,{label:'Row Border Radius', value:a.rowRadius, min:0, max:24, step:1, onChange:function(v){set({rowRadius:v});}}),
185 el(RangeControl,{label:'Max Width (px)', value:a.maxWidth, min:340,max:960,step:10,onChange:function(v){set({maxWidth:v});}}),
186 el(RangeControl,{label:'Padding Top (px)', value:a.paddingTop, min:0, max:160,step:4, onChange:function(v){set({paddingTop:v});}}),
187 el(RangeControl,{label:'Padding Bottom (px)',value:a.paddingBottom,min:0, max:160,step:4, onChange:function(v){set({paddingBottom:v});}})
188 )
189 ),
190 el('div', blockProps, el(TZPreview, {attributes:a, setAttributes:set}))
191 );
192 },
193 save: function(props) {
194 var a = props.attributes;
195 var bp = wp.blockEditor.useBlockProps.save((function () {
196 var tv = Object.assign({}, _tvf(a.titleTypo, '--bktzc-tt-'), _tvf(a.timeTypo, '--bktzc-tm-'));
197 return { style: tv };
198 })());
199 return el('div', bp, el('div', {className:'bkbg-tzc-app','data-opts':JSON.stringify(a)}));
200 }
201 });
202 }() );
203