| 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 |
|