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 / okr-tracker / index.js

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

218 lines 16.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 ( function () {
2 const el = window.wp.element.createElement;
3 const { registerBlockType } = window.wp.blocks;
4 const { InspectorControls, useBlockProps, PanelColorSettings } = window.wp.blockEditor;
5 const { PanelBody, RangeControl, ToggleControl, TextControl, SelectControl, Button } = window.wp.components;
6 const { __ } = window.wp.i18n;
7
8 var _tc, _tv;
9 function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
10 function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
11
12 // ── Helpers ───────────────────────────────────────────────────────────────
13 const STATUS_COLORS = { 'done': '#10b981', 'on-track': '#6366f1', 'at-risk': '#f59e0b', 'off-track': '#ef4444' };
14 const STATUS_LABELS = { 'done': 'Done', 'on-track': 'On Track', 'at-risk': 'At Risk', 'off-track': 'Off Track' };
15
16 function avgProgress( krs ) {
17 if ( ! krs || ! krs.length ) return 0;
18 return Math.round( krs.reduce( ( s, kr ) => s + ( kr.progress || 0 ), 0 ) / krs.length );
19 }
20
21 // ── Renderer ──────────────────────────────────────────────────────────────
22 function renderOKR( a ) {
23 const objectives = a.objectives || [];
24
25 return el( 'div', { className: 'bkbg-okr-objectives', style: { display: 'flex', flexDirection: 'column', gap: 16 } },
26 objectives.map( ( obj, oi ) => {
27 const avg = avgProgress( obj.keyResults );
28 const clr = obj.color || '#6366f1';
29
30 return el( 'div', {
31 key: oi,
32 className: 'bkbg-okr-objective',
33 style: {
34 background: a.cardBg,
35 borderRadius: a.borderRadius + 'px',
36 padding: a.cardPadding + 'px',
37 boxSizing: 'border-box',
38 borderLeft: `4px solid ${ clr }`,
39 }
40 },
41 // Objective header
42 el( 'div', { className: 'bkbg-okr-obj-header', style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 } },
43 el( 'span', { className: 'bkbg-okr-obj-label', style: { color: a.titleColor } }, obj.label ),
44 el( 'span', { className: 'bkbg-okr-obj-pct', style: { color: clr } }, avg + '%' ),
45 ),
46
47 // Objective progress bar
48 a.showProgress && el( 'div', { style: { marginBottom: 14 } },
49 el( 'div', { style: { height: a.barHeight + 'px', background: a.trackColor, borderRadius: 99 + 'px', overflow: 'hidden' } },
50 el( 'div', { style: { height: '100%', width: avg + '%', background: clr, borderRadius: 99 + 'px', transition: 'width 0.4s' } } )
51 )
52 ),
53
54 // Key Results
55 el( 'div', { className: 'bkbg-okr-krs', style: { display: 'flex', flexDirection: 'column', gap: 10 } },
56 ( obj.keyResults || [] ).map( ( kr, ki ) => {
57 const stClr = STATUS_COLORS[ kr.status ] || '#6b7280';
58 return el( 'div', { key: ki, className: 'bkbg-okr-kr' },
59 el( 'div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 4 } },
60 el( 'span', { className: 'bkbg-okr-kr-label', style: { color: a.textColor } }, kr.label ),
61 el( 'div', { style: { display: 'flex', alignItems: 'center', gap: 8, flexShrink: 0 } },
62 el( 'span', { style: { fontSize: 12, color: a.mutedColor, fontWeight: 600 } }, ( kr.progress || 0 ) + '%' ),
63 a.showStatus && el( 'span', { style: { fontSize: 11, fontWeight: 600, color: stClr, background: stClr + '18', borderRadius: 99 + 'px', padding: '2px 8px', whiteSpace: 'nowrap' } }, STATUS_LABELS[ kr.status ] || kr.status ),
64 ),
65 ),
66 a.showProgress && el( 'div', { style: { height: ( a.barHeight - 2 ) + 'px', background: a.trackColor, borderRadius: 99 + 'px', overflow: 'hidden' } },
67 el( 'div', { style: { height: '100%', width: ( kr.progress || 0 ) + '%', background: stClr, borderRadius: 99 + 'px' } } )
68 ),
69 );
70 } )
71 ),
72 );
73 } )
74 );
75 }
76
77 // ── Mutations ─────────────────────────────────────────────────────────────
78 function updObj( setAttributes, objectives, oi, field, val ) {
79 setAttributes( { objectives: objectives.map( ( o, i ) => i === oi ? { ...o, [field]: val } : o ) } );
80 }
81 function updKR( setAttributes, objectives, oi, ki, field, val ) {
82 setAttributes( { objectives: objectives.map( ( o, i ) => i !== oi ? o : {
83 ...o,
84 keyResults: ( o.keyResults || [] ).map( ( kr, j ) => j === ki ? { ...kr, [field]: val } : kr ),
85 } ) } );
86 }
87
88 // ── Block ─────────────────────────────────────────────────────────────────
89 registerBlockType( 'blockenberg/okr-tracker', {
90 edit: function ( props ) {
91 const { attributes: a, setAttributes } = props;
92 const blockProps = (function () {
93 var bp = useBlockProps( { className: 'bkbg-okr-wrap' } );
94 var _tvf = getTypoCssVars();
95 var s = { background: a.bgColor, padding: '16px', borderRadius: 8, boxSizing: 'border-box' };
96 if (_tvf) {
97 Object.assign(s, _tvf(a.titleTypo, '--bkbg-okr-tt-'));
98 Object.assign(s, _tvf(a.objLabelTypo, '--bkbg-okr-ol-'));
99 Object.assign(s, _tvf(a.krTypo, '--bkbg-okr-kr-'));
100 }
101 bp.style = Object.assign(s, bp.style || {});
102 return bp;
103 }());
104
105 return el( 'div', blockProps,
106 el( InspectorControls, {},
107
108 el( PanelBody, { title: __( 'Settings', 'blockenberg' ), initialOpen: true },
109 el( TextControl, { label: __( 'Title', 'blockenberg' ), value: a.title, onChange: v => setAttributes( { title: v } ) } ),
110 el( ToggleControl, { label: __( 'Show Title', 'blockenberg' ), checked: a.showTitle, onChange: v => setAttributes( { showTitle: v } ) } ),
111 el( ToggleControl, { label: __( 'Show Period', 'blockenberg' ), checked: a.showPeriod, onChange: v => setAttributes( { showPeriod: v } ) } ),
112 a.showPeriod && el( TextControl, { label: __( 'Period Label', 'blockenberg' ), value: a.period, onChange: v => setAttributes( { period: v } ) } ),
113 el( ToggleControl, { label: __( 'Show Progress Bars', 'blockenberg' ), checked: a.showProgress, onChange: v => setAttributes( { showProgress: v } ) } ),
114 el( ToggleControl, { label: __( 'Show Status Badges', 'blockenberg' ),checked: a.showStatus, onChange: v => setAttributes( { showStatus: v } ) } ),
115 ),
116
117 el( PanelBody, { title: __( 'Style', 'blockenberg' ), initialOpen: false },
118 el( RangeControl, { label: __( 'Bar Height', 'blockenberg' ), value: a.barHeight, onChange: v => setAttributes( { barHeight: v } ), min: 4, max: 20 } ),
119 el( RangeControl, { label: __( 'Border Radius', 'blockenberg' ), value: a.borderRadius, onChange: v => setAttributes( { borderRadius: v } ), min: 0, max: 24 } ),
120 el( RangeControl, { label: __( 'Card Padding', 'blockenberg' ), value: a.cardPadding, onChange: v => setAttributes( { cardPadding: v } ), min: 8, max: 48 } ),
121 ),
122
123 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
124 el( getTypoControl(), { label: __( 'Title' ), value: a.titleTypo, onChange: v => setAttributes( { titleTypo: v } ) } ),
125 el( getTypoControl(), { label: __( 'Objective Label' ), value: a.objLabelTypo, onChange: v => setAttributes( { objLabelTypo: v } ) } ),
126 el( getTypoControl(), { label: __( 'Key Result' ), value: a.krTypo, onChange: v => setAttributes( { krTypo: v } ) } )
127 ),
128 el( PanelColorSettings, {
129 title: __( 'Colors', 'blockenberg' ), initialOpen: false,
130 colorSettings: [
131 { label: __( 'Wrapper BG', 'blockenberg' ), value: a.bgColor, onChange: v => setAttributes( { bgColor: v || '#ffffff' } ) },
132 { label: __( 'Card BG', 'blockenberg' ), value: a.cardBg, onChange: v => setAttributes( { cardBg: v || '#f9fafb' } ) },
133 { label: __( 'Title', 'blockenberg' ), value: a.titleColor, onChange: v => setAttributes( { titleColor: v || '#111827' } ) },
134 { label: __( 'Text', 'blockenberg' ), value: a.textColor, onChange: v => setAttributes( { textColor: v || '#374151' } ) },
135 { label: __( 'Muted', 'blockenberg' ), value: a.mutedColor, onChange: v => setAttributes( { mutedColor: v || '#6b7280' } ) },
136 { label: __( 'Track', 'blockenberg' ), value: a.trackColor, onChange: v => setAttributes( { trackColor: v || '#e5e7eb' } ) },
137 ]
138 } ),
139
140 el( PanelBody, { title: __( 'Objectives', 'blockenberg' ), initialOpen: false },
141 el( Button, { variant: 'secondary', style: { marginBottom: 10 }, onClick: () => setAttributes( { objectives: [ ...( a.objectives || [] ), { label: 'New Objective', color: '#6366f1', keyResults: [ { label: 'Key Result 1', progress: 0, status: 'on-track' } ] } ] } ) }, __( '+ Add Objective', 'blockenberg' ) ),
142
143 a.objectives.map( ( obj, oi ) =>
144 el( PanelBody, { key: oi, title: obj.label.length > 30 ? obj.label.slice( 0, 28 ) + '' : obj.label, initialOpen: false },
145 el( TextControl, { label: __( 'Label', 'blockenberg' ), value: obj.label, onChange: v => updObj( setAttributes, a.objectives, oi, 'label', v ) } ),
146 el( 'div', { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 } },
147 el( 'label', { style: { fontSize: 12 } }, __( 'Color', 'blockenberg' ) ),
148 el( 'input', { type: 'color', value: obj.color, style: { width: 40, height: 28, border: 'none', borderRadius: 4, cursor: 'pointer' }, onChange: e => updObj( setAttributes, a.objectives, oi, 'color', e.target.value ) } ),
149 ),
150 el( 'strong', { style: { display: 'block', fontSize: 12, marginBottom: 6 } }, __( 'Key Results', 'blockenberg' ) ),
151 el( Button, { variant: 'secondary', isSmall: true, style: { marginBottom: 8 }, onClick: () => updObj( setAttributes, a.objectives, oi, 'keyResults', [ ...( obj.keyResults || [] ), { label: 'New Key Result', progress: 0, status: 'on-track' } ] ) }, __( '+ Add KR', 'blockenberg' ) ),
152 ( obj.keyResults || [] ).map( ( kr, ki ) =>
153 el( 'div', { key: ki, style: { background: '#f3f4f6', borderRadius: 6, padding: '10px', marginBottom: 6 } },
154 el( TextControl, { label: __( 'Label', 'blockenberg' ), value: kr.label, onChange: v => updKR( setAttributes, a.objectives, oi, ki, 'label', v ) } ),
155 el( RangeControl, { label: __( 'Progress %', 'blockenberg' ), value: kr.progress, onChange: v => updKR( setAttributes, a.objectives, oi, ki, 'progress', v ), min: 0, max: 100 } ),
156 el( SelectControl, {
157 label: __( 'Status', 'blockenberg' ),
158 value: kr.status,
159 options: [
160 { label: __( 'On Track', 'blockenberg' ), value: 'on-track' },
161 { label: __( 'At Risk', 'blockenberg' ), value: 'at-risk' },
162 { label: __( 'Off Track', 'blockenberg' ), value: 'off-track' },
163 { label: __( 'Done', 'blockenberg' ), value: 'done' },
164 ],
165 onChange: v => updKR( setAttributes, a.objectives, oi, ki, 'status', v ),
166 } ),
167 el( Button, { isDestructive: true, isSmall: true, onClick: () => updObj( setAttributes, a.objectives, oi, 'keyResults', ( obj.keyResults || [] ).filter( ( _, j ) => j !== ki ) ) }, __( 'Remove KR', 'blockenberg' ) ),
168 )
169 ),
170 el( Button, { isDestructive: true, isSmall: true, style: { marginTop: 6 }, onClick: () => setAttributes( { objectives: a.objectives.filter( ( _, x ) => x !== oi ) } ) }, __( 'Remove Objective', 'blockenberg' ) ),
171 )
172 ),
173 ),
174 ),
175
176 // Title & period
177 el( 'div', { className: 'bkbg-okr-header', style: { marginBottom: 20 } },
178 a.showTitle && a.title && el( 'h3', { className: 'bkbg-okr-title', style: { color: a.titleColor, margin: '0 0 4px' } }, a.title ),
179 a.showPeriod && a.period && el( 'div', { className: 'bkbg-okr-period', style: { color: a.mutedColor } }, a.period ),
180 ),
181
182 renderOKR( a ),
183 );
184 },
185
186 save: function ( { attributes: a } ) {
187 var _tvf = getTypoCssVars();
188 var s = { background: a.bgColor, padding: '16px', borderRadius: 8, boxSizing: 'border-box' };
189 if (_tvf) {
190 Object.assign(s, _tvf(a.titleTypo, '--bkbg-okr-tt-'));
191 Object.assign(s, _tvf(a.objLabelTypo, '--bkbg-okr-ol-'));
192 Object.assign(s, _tvf(a.krTypo, '--bkbg-okr-kr-'));
193 }
194 const blockProps = useBlockProps.save( { className: 'bkbg-okr-wrap', style: s } );
195 return el( 'div', blockProps,
196 el( 'div', { className: 'bkbg-okr-header', style: { marginBottom: 20 } },
197 a.showTitle && a.title ? el( 'h3', { className: 'bkbg-okr-title', style: { color: a.titleColor, margin: '0 0 4px' } }, a.title ) : null,
198 a.showPeriod && a.period ? el( 'div', { className: 'bkbg-okr-period', style: { color: a.mutedColor } }, a.period ) : null,
199 ),
200 renderOKR( a ),
201 );
202 },
203
204 deprecated: [{
205 save: function ( { attributes: a } ) {
206 const blockProps = useBlockProps.save( { className: 'bkbg-okr-wrap', style: { background: a.bgColor, padding: '16px', borderRadius: 8, boxSizing: 'border-box' } } );
207 return el( 'div', blockProps,
208 el( 'div', { className: 'bkbg-okr-header', style: { marginBottom: 20 } },
209 a.showTitle && a.title ? el( 'h3', { className: 'bkbg-okr-title', style: { color: a.titleColor, margin: '0 0 4px', fontSize: ( a.labelFontSize + 4 ) + 'px' } }, a.title ) : null,
210 a.showPeriod && a.period ? el( 'div', { style: { fontSize: a.periodSize || 13, color: a.mutedColor } }, a.period ) : null,
211 ),
212 renderOKR( a ),
213 );
214 }
215 }],
216 } );
217 }() );
218