From 8137327f1fddf8edfeaded68a110ca4891383ca6 Mon Sep 17 00:00:00 2001 From: Chris Abraham Date: Fri, 2 Oct 2026 14:30:55 +0700 Subject: [PATCH] Enhance color management in Sessionize block editor with curated track colors and improved color selection UI Signed-off-by: Chris Abraham --- .../sessionize-schedule/build/index.asset.php | 2 +- .../blocks/sessionize-schedule/build/index.js | 2 +- .../blocks/sessionize-schedule/src/edit.js | 147 ++++++++++++++---- 3 files changed, 118 insertions(+), 33 deletions(-) diff --git a/web/wp-content/plugins/sessionize-blocks/blocks/sessionize-schedule/build/index.asset.php b/web/wp-content/plugins/sessionize-blocks/blocks/sessionize-schedule/build/index.asset.php index f76341df..67bb9fde 100644 --- a/web/wp-content/plugins/sessionize-blocks/blocks/sessionize-schedule/build/index.asset.php +++ b/web/wp-content/plugins/sessionize-blocks/blocks/sessionize-schedule/build/index.asset.php @@ -1 +1 @@ - array('react', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element'), 'version' => 'fa469e82610e3cb66ae0'); + array('react', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element'), 'version' => '49a561a58f72c25585b5'); diff --git a/web/wp-content/plugins/sessionize-blocks/blocks/sessionize-schedule/build/index.js b/web/wp-content/plugins/sessionize-blocks/blocks/sessionize-schedule/build/index.js index ec700c1f..3aacb818 100644 --- a/web/wp-content/plugins/sessionize-blocks/blocks/sessionize-schedule/build/index.js +++ b/web/wp-content/plugins/sessionize-blocks/blocks/sessionize-schedule/build/index.js @@ -1 +1 @@ -(()=>{"use strict";var e,t={285(){const e=window.wp.blocks,t=window.React,n=window.wp.blockEditor,l=window.wp.components,o=window.wp.element,a=JSON.parse('{"UU":"custom/sessionize-schedule"}');(0,e.registerBlockType)(a.UU,{edit:function({attributes:e,setAttributes:a}){const{apiCode:i,publicSlug:r,primaryFilterTitle:s,timeFormat:d,dateFormat:c,enableGridView:p,enablePersonalAgenda:m,defaultShowAllDays:u,hideTopControls:h,hideSessionTimes:g,speakerTitleQuestionId:C,speakerCompanyQuestionId:b,speakerCompanyOverrideQuestionId:y,cardSpeakerOverrideQuestionId:k,presentationSlidesQuestionId:f,customLinkField1QuestionId:v,customLinkField2QuestionId:S,customLinkField3QuestionId:T,customLinkField4QuestionId:E,customLinkField5QuestionId:x,hiddenFilterCategories:w,hideSessionChipsForCategories:F,hideAllChipsForPrimaryValues:O,includeSpeakerTitleForPrimaryValues:I,companyRollupNames:P,primaryColorOverrides:Q}=e,A=(0,n.useBlockProps)();let z={};try{z=Q?JSON.parse(Q):{}}catch(e){z={}}const L=Object.entries(z),[B,D]=(0,o.useState)(null),[N,V]=(0,o.useState)("");return(0,t.createElement)("div",{...A},(0,t.createElement)(n.InspectorControls,null,(0,t.createElement)(l.PanelBody,{title:"Sessionize Configuration",initialOpen:!0},(0,t.createElement)(l.TextControl,{label:"Sessionize API Code *",value:i,onChange:e=>a({apiCode:e}),help:"Your unique Sessionize endpoint ID. Found under API / Embed in Sessionize."}),(0,t.createElement)(l.TextControl,{label:"Sessionize Public Slug *",value:r,onChange:e=>a({publicSlug:e}),help:"Your event's Sessionize URL slug (e.g., kubecon-cloudnativecon-japan-2026)."}),(0,t.createElement)(l.TextControl,{label:"Primary Category Name *",value:s,onChange:e=>a({primaryFilterTitle:e}),help:"The exact Question/Title of the Sessionize field that contains your main session groupings. Drives color-coding and the first filter chip."}),(0,t.createElement)(l.SelectControl,{label:"Time Format",value:d,options:[{label:"12-Hour (AM/PM)",value:"12h"},{label:"24-Hour",value:"24h"}],onChange:e=>a({timeFormat:e})}),(0,t.createElement)(l.SelectControl,{label:"Date Format",value:c,options:[{label:"Day/Month/Year",value:"dmy"},{label:"Month/Day/Year",value:"mdy"}],onChange:e=>a({dateFormat:e})})),(0,t.createElement)(l.PanelBody,{title:"Display Options",initialOpen:!1},(0,t.createElement)(l.ToggleControl,{label:"Enable Grid, List, and Speaker Views",checked:p,onChange:e=>a({enableGridView:e}),help:"If off, attendees only see the list view with no toggle."}),(0,t.createElement)(l.ToggleControl,{label:"Allow Attendees to Save Sessions",checked:m,onChange:e=>a({enablePersonalAgenda:e}),help:"Lets attendees star sessions and build a personal agenda in their browser."}),(0,t.createElement)(l.ToggleControl,{label:"Show All Conference Days at Once",checked:u,onChange:e=>a({defaultShowAllDays:e}),help:"If off, the schedule splits by day."}),(0,t.createElement)(l.ToggleControl,{label:"Hide Filters and Search Bar",checked:h,onChange:e=>a({hideTopControls:e}),help:"Hides all filter chips and search from attendees."}),(0,t.createElement)(l.ToggleControl,{label:"Hide Session Times (Dates Only)",checked:g,onChange:e=>a({hideSessionTimes:e}),help:"Groups each day's sessions under a single date heading instead of printing a time on every session. Session order is unaffected — only the printed time is hidden. If Grid view is enabled above, it keeps its own time axis regardless of this setting, since a grid is inherently time-based."})),(0,t.createElement)(l.PanelBody,{title:"Speaker and Session Data Fields",initialOpen:!1},(0,t.createElement)("p",{className:"components-base-control__help",style:{marginTop:0}},"Enter the exact Question/Title of each field as it appears in your Sessionize form."),(0,t.createElement)(l.TextControl,{label:"Speaker Title Field *",value:C,onChange:e=>a({speakerTitleQuestionId:e}),help:"Submission field where speakers enter their job title."}),(0,t.createElement)(l.TextControl,{label:"Speaker Company Field *",value:b,onChange:e=>a({speakerCompanyQuestionId:e}),help:"Submission field where speakers enter their organization."}),(0,t.createElement)(l.TextControl,{label:"Speaker Company Override Field",value:y,onChange:e=>a({speakerCompanyOverrideQuestionId:e}),help:"Internal field used to display a role (e.g., Program Chair) instead of company name on the session card."}),(0,t.createElement)(l.TextControl,{label:"Card Speaker Display Override",value:k,onChange:e=>a({cardSpeakerOverrideQuestionId:e}),help:"Internal field where organizers enter a comma-separated list of speaker names to show on the session card."}),(0,t.createElement)(l.TextControl,{label:"Presentation Slides Field *",value:f,onChange:e=>a({presentationSlidesQuestionId:e}),help:"Additional field where speakers link their slide deck. Adds a Slides button to the session popup."})),(0,t.createElement)(l.PanelBody,{title:"Custom Session Links",initialOpen:!1},(0,t.createElement)("p",{className:"components-base-control__help",style:{marginTop:0}},"Enter the exact Question/Title of a web address session field in Sessionize. The field's title appears as the button label on the session popup."),(0,t.createElement)(l.TextControl,{label:"Custom Link 1",value:v,onChange:e=>a({customLinkField1QuestionId:e})}),(0,t.createElement)(l.TextControl,{label:"Custom Link 2",value:S,onChange:e=>a({customLinkField2QuestionId:e})}),(0,t.createElement)(l.TextControl,{label:"Custom Link 3",value:T,onChange:e=>a({customLinkField3QuestionId:e})}),(0,t.createElement)(l.TextControl,{label:"Custom Link 4",value:E,onChange:e=>a({customLinkField4QuestionId:e})}),(0,t.createElement)(l.TextControl,{label:"Custom Link 5",value:x,onChange:e=>a({customLinkField5QuestionId:e})})),(0,t.createElement)(l.PanelBody,{title:"Filtering and Visibility",initialOpen:!1},(0,t.createElement)("p",{className:"components-base-control__help",style:{marginTop:0}},"All fields in this section accept comma-separated values."),(0,t.createElement)(l.TextControl,{label:"Categories to Hide from Filters",value:w,onChange:e=>a({hiddenFilterCategories:e}),help:"Category names that will not appear as filter options at the top. Tags still show on session cards."}),(0,t.createElement)(l.TextControl,{label:"Hide Tags on Session Cards",value:F,onChange:e=>a({hideSessionChipsForCategories:e}),help:"Category names whose tag badges will not appear on session cards. Still filterable at the top."}),(0,t.createElement)(l.TextControl,{label:"Hide All Tags for These Primary Values",value:O,onChange:e=>a({hideAllChipsForPrimaryValues:e}),help:"Primary category values where no tag badges appear on the session card (e.g., Breaks, Registration)."}),(0,t.createElement)(l.TextControl,{label:"Show Speaker Title for These Primary Values",value:I,onChange:e=>a({includeSpeakerTitleForPrimaryValues:e}),help:"Primary category values where speaker job titles appear on session cards. Typically used for Keynote Sessions."}),(0,t.createElement)(l.TextControl,{label:"Sponsor Company Rollup",value:P,onChange:e=>a({companyRollupNames:e}),help:"Sponsor company names. Adds a More from [Company] section in the speaker profile. Sponsor benefit."})),(0,t.createElement)(l.PanelBody,{title:"Track Color Overrides",initialOpen:!1},(0,t.createElement)("p",{className:"components-base-control__help",style:{marginTop:0}},"Type the exact track name as it appears in Sessionize, then click the swatch to assign a color. Tracks not listed here are auto-colored."),L.map(([e,n])=>(0,t.createElement)("div",{key:e,style:{marginBottom:"12px"}},(0,t.createElement)(l.Flex,{align:"center"},(0,t.createElement)(l.FlexItem,null,(0,t.createElement)("button",{type:"button",onClick:()=>D(B===e?null:e),style:{width:"28px",height:"28px",borderRadius:"4px",border:"1px solid #ccc",backgroundColor:n,cursor:"pointer",padding:0},"aria-label":`Change color for ${e}`})),(0,t.createElement)(l.FlexBlock,null,(0,t.createElement)("span",{style:{fontSize:"13px"}},e)),(0,t.createElement)(l.FlexItem,null,(0,t.createElement)(l.Button,{isDestructive:!0,variant:"tertiary",size:"small",onClick:()=>function(e){const t={...z};delete t[e],a({primaryColorOverrides:JSON.stringify(t)}),B===e&&D(null)}(e),"aria-label":`Remove ${e}`},"✕"))),B===e&&(0,t.createElement)("div",{style:{marginTop:"8px"}},(0,t.createElement)(l.ColorPicker,{color:n,onChange:t=>function(e,t){const n={...z,[e]:t};a({primaryColorOverrides:JSON.stringify(n)})}(e,t),enableAlpha:!1})))),(0,t.createElement)("div",{style:{marginTop:"16px",borderTop:"1px solid #e0e0e0",paddingTop:"12px"}},(0,t.createElement)(l.Flex,{align:"flex-end"},(0,t.createElement)(l.FlexBlock,null,(0,t.createElement)(l.TextControl,{label:"New entry label",value:N,onChange:V,placeholder:"e.g., Keynote Sessions"})),(0,t.createElement)(l.FlexItem,null,(0,t.createElement)(l.Button,{variant:"secondary",size:"compact",onClick:function(){const e=N.trim();if(!e||z.hasOwnProperty(e))return;const t={...z,[e]:"#000000"};a({primaryColorOverrides:JSON.stringify(t)}),V(""),D(e)},disabled:!N.trim(),style:{marginBottom:"8px"}},"Add")))))),(0,t.createElement)(l.Placeholder,{icon:"calendar-alt",label:"Sessionize Schedule Block",instructions:`Configured for API code: ${i}. Configure further settings in the block sidebar.`}))}})}},n={};function l(e){var o=n[e];if(void 0!==o)return o.exports;var a=n[e]={exports:{}};return t[e](a,a.exports,l),a.exports}l.m=t,e=[],l.O=(t,n,o,a)=>{if(!n){var i=1/0;for(c=0;c=a)&&Object.keys(l.O).every(e=>l.O[e](n[s]))?n.splice(s--,1):(r=!1,a0&&e[c-1][2]>a;c--)e[c]=e[c-1];e[c]=[n,o,a]},l.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={57:0,350:0};l.O.j=t=>0===e[t];var t=(t,n)=>{var o,a,[i,r,s]=n,d=0;if(i.some(t=>0!==e[t])){for(o in r)l.o(r,o)&&(l.m[o]=r[o]);if(s)var c=s(l)}for(t&&t(n);dl(285));o=l.O(o)})(); \ No newline at end of file +(()=>{"use strict";var e,t={285(){const e=window.wp.blocks,t=window.React,l=window.wp.blockEditor,n=window.wp.components,a=window.wp.element,o=[{label:"Scarlet",hue:13,hex:"#d94d26"},{label:"Vermilion",hue:25,hex:"#d97126"},{label:"Orange",hue:35,hex:"#d98e26"},{label:"Amber",hue:45,hex:"#d9ac26"},{label:"Yellow",hue:57,hex:"#d9d026"},{label:"Lime",hue:77,hex:"#a6d926"},{label:"Green",hue:120,hex:"#26d926"},{label:"Emerald",hue:153,hex:"#26d988"},{label:"Teal",hue:171,hex:"#26d9be"},{label:"Aqua",hue:183,hex:"#26d0d9"},{label:"Cyan",hue:192,hex:"#26b5d9"},{label:"Sky",hue:202,hex:"#2697d9"},{label:"Azure",hue:213,hex:"#2677d9"},{label:"Cobalt",hue:224,hex:"#2656d9"},{label:"Blue",hue:241,hex:"#2926d9"},{label:"Violet",hue:274,hex:"#8b26d9"},{label:"Orchid",hue:295,hex:"#ca26d9"},{label:"Fuchsia",hue:321,hex:"#d9269a"},{label:"Pink",hue:339,hex:"#d92665"},{label:"Crimson",hue:353,hex:"#d9263b"}];function i(e){const t=String(e||"").toLowerCase();return o.find(e=>e.hex===t)||null}function r(e){const t=i(e),l={width:"28px",height:"28px",borderRadius:"4px",boxSizing:"border-box",flexShrink:0};return t?{...l,background:`hsl(${t.hue} 70% 95%)`,border:`1px solid hsl(${t.hue} 70% 50%)`,borderLeftWidth:"5px"}:{...l,background:e,border:"1px solid #ccc"}}const s=JSON.parse('{"UU":"custom/sessionize-schedule"}');(0,e.registerBlockType)(s.UU,{edit:function({attributes:e,setAttributes:s}){const{apiCode:d,publicSlug:c,primaryFilterTitle:h,timeFormat:u,dateFormat:p,enableGridView:m,enablePersonalAgenda:b,defaultShowAllDays:g,hideTopControls:C,hideSessionTimes:y,speakerTitleQuestionId:f,speakerCompanyQuestionId:x,speakerCompanyOverrideQuestionId:k,cardSpeakerOverrideQuestionId:v,presentationSlidesQuestionId:S,customLinkField1QuestionId:T,customLinkField2QuestionId:E,customLinkField3QuestionId:w,customLinkField4QuestionId:F,customLinkField5QuestionId:O,hiddenFilterCategories:I,hideSessionChipsForCategories:P,hideAllChipsForPrimaryValues:L,includeSpeakerTitleForPrimaryValues:A,companyRollupNames:Q,primaryColorOverrides:z}=e,B=(0,l.useBlockProps)();let N={};try{N=z?JSON.parse(z):{}}catch(e){N={}}const D=Object.entries(N),[V,_]=(0,a.useState)("");function H(e,t){const l=e.toLowerCase(),n=D.find(([e,n])=>e!==t&&String(n).toLowerCase()===l);return n?n[0]:null}function R(e,t){const l=o.map(t=>{const l=H(t.hex,e);return{value:t.hex,label:l?`${t.label} (used by ${l})`:t.label}});return t&&!i(t)&&l.unshift({value:t,label:`Custom (${t})`}),l}return(0,t.createElement)("div",{...B},(0,t.createElement)(l.InspectorControls,null,(0,t.createElement)(n.PanelBody,{title:"Sessionize Configuration",initialOpen:!0},(0,t.createElement)(n.TextControl,{label:"Sessionize API Code *",value:d,onChange:e=>s({apiCode:e}),help:"Your unique Sessionize endpoint ID. Found under API / Embed in Sessionize."}),(0,t.createElement)(n.TextControl,{label:"Sessionize Public Slug *",value:c,onChange:e=>s({publicSlug:e}),help:"Your event's Sessionize URL slug (e.g., kubecon-cloudnativecon-japan-2026)."}),(0,t.createElement)(n.TextControl,{label:"Primary Category Name *",value:h,onChange:e=>s({primaryFilterTitle:e}),help:"The exact Question/Title of the Sessionize field that contains your main session groupings. Drives color-coding and the first filter chip."}),(0,t.createElement)(n.SelectControl,{label:"Time Format",value:u,options:[{label:"12-Hour (AM/PM)",value:"12h"},{label:"24-Hour",value:"24h"}],onChange:e=>s({timeFormat:e})}),(0,t.createElement)(n.SelectControl,{label:"Date Format",value:p,options:[{label:"Day/Month/Year",value:"dmy"},{label:"Month/Day/Year",value:"mdy"}],onChange:e=>s({dateFormat:e})})),(0,t.createElement)(n.PanelBody,{title:"Display Options",initialOpen:!1},(0,t.createElement)(n.ToggleControl,{label:"Enable Grid, List, and Speaker Views",checked:m,onChange:e=>s({enableGridView:e}),help:"If off, attendees only see the list view with no toggle."}),(0,t.createElement)(n.ToggleControl,{label:"Allow Attendees to Save Sessions",checked:b,onChange:e=>s({enablePersonalAgenda:e}),help:"Lets attendees star sessions and build a personal agenda in their browser."}),(0,t.createElement)(n.ToggleControl,{label:"Show All Conference Days at Once",checked:g,onChange:e=>s({defaultShowAllDays:e}),help:"If off, the schedule splits by day."}),(0,t.createElement)(n.ToggleControl,{label:"Hide Filters and Search Bar",checked:C,onChange:e=>s({hideTopControls:e}),help:"Hides all filter chips and search from attendees."}),(0,t.createElement)(n.ToggleControl,{label:"Hide Session Times (Dates Only)",checked:y,onChange:e=>s({hideSessionTimes:e}),help:"Groups each day's sessions under a single date heading instead of printing a time on every session. Session order is unaffected — only the printed time is hidden. If Grid view is enabled above, it keeps its own time axis regardless of this setting, since a grid is inherently time-based."})),(0,t.createElement)(n.PanelBody,{title:"Speaker and Session Data Fields",initialOpen:!1},(0,t.createElement)("p",{className:"components-base-control__help",style:{marginTop:0}},"Enter the exact Question/Title of each field as it appears in your Sessionize form."),(0,t.createElement)(n.TextControl,{label:"Speaker Title Field *",value:f,onChange:e=>s({speakerTitleQuestionId:e}),help:"Submission field where speakers enter their job title."}),(0,t.createElement)(n.TextControl,{label:"Speaker Company Field *",value:x,onChange:e=>s({speakerCompanyQuestionId:e}),help:"Submission field where speakers enter their organization."}),(0,t.createElement)(n.TextControl,{label:"Speaker Company Override Field",value:k,onChange:e=>s({speakerCompanyOverrideQuestionId:e}),help:"Internal field used to display a role (e.g., Program Chair) instead of company name on the session card."}),(0,t.createElement)(n.TextControl,{label:"Card Speaker Display Override",value:v,onChange:e=>s({cardSpeakerOverrideQuestionId:e}),help:"Internal field where organizers enter a comma-separated list of speaker names to show on the session card."}),(0,t.createElement)(n.TextControl,{label:"Presentation Slides Field *",value:S,onChange:e=>s({presentationSlidesQuestionId:e}),help:"Additional field where speakers link their slide deck. Adds a Slides button to the session popup."})),(0,t.createElement)(n.PanelBody,{title:"Custom Session Links",initialOpen:!1},(0,t.createElement)("p",{className:"components-base-control__help",style:{marginTop:0}},"Enter the exact Question/Title of a web address session field in Sessionize. The field's title appears as the button label on the session popup."),(0,t.createElement)(n.TextControl,{label:"Custom Link 1",value:T,onChange:e=>s({customLinkField1QuestionId:e})}),(0,t.createElement)(n.TextControl,{label:"Custom Link 2",value:E,onChange:e=>s({customLinkField2QuestionId:e})}),(0,t.createElement)(n.TextControl,{label:"Custom Link 3",value:w,onChange:e=>s({customLinkField3QuestionId:e})}),(0,t.createElement)(n.TextControl,{label:"Custom Link 4",value:F,onChange:e=>s({customLinkField4QuestionId:e})}),(0,t.createElement)(n.TextControl,{label:"Custom Link 5",value:O,onChange:e=>s({customLinkField5QuestionId:e})})),(0,t.createElement)(n.PanelBody,{title:"Filtering and Visibility",initialOpen:!1},(0,t.createElement)("p",{className:"components-base-control__help",style:{marginTop:0}},"All fields in this section accept comma-separated values."),(0,t.createElement)(n.TextControl,{label:"Categories to Hide from Filters",value:I,onChange:e=>s({hiddenFilterCategories:e}),help:"Category names that will not appear as filter options at the top. Tags still show on session cards."}),(0,t.createElement)(n.TextControl,{label:"Hide Tags on Session Cards",value:P,onChange:e=>s({hideSessionChipsForCategories:e}),help:"Category names whose tag badges will not appear on session cards. Still filterable at the top."}),(0,t.createElement)(n.TextControl,{label:"Hide All Tags for These Primary Values",value:L,onChange:e=>s({hideAllChipsForPrimaryValues:e}),help:"Primary category values where no tag badges appear on the session card (e.g., Breaks, Registration)."}),(0,t.createElement)(n.TextControl,{label:"Show Speaker Title for These Primary Values",value:A,onChange:e=>s({includeSpeakerTitleForPrimaryValues:e}),help:"Primary category values where speaker job titles appear on session cards. Typically used for Keynote Sessions."}),(0,t.createElement)(n.TextControl,{label:"Sponsor Company Rollup",value:Q,onChange:e=>s({companyRollupNames:e}),help:"Sponsor company names. Adds a More from [Company] section in the speaker profile. Sponsor benefit."})),(0,t.createElement)(n.PanelBody,{title:"Track Color Overrides",initialOpen:!1},(0,t.createElement)("p",{className:"components-base-control__help",style:{marginTop:0}},"Type the exact track name as it appears in Sessionize, then choose a color. Tracks not listed here are auto-colored."),D.map(([e,l])=>(0,t.createElement)("div",{key:e,style:{marginBottom:"12px"}},(0,t.createElement)("div",{style:{fontSize:"13px",fontWeight:500,marginBottom:"4px"}},e),(0,t.createElement)(n.Flex,{align:"center"},(0,t.createElement)(n.FlexItem,null,(0,t.createElement)("span",{"aria-hidden":"true",style:{display:"block",...r(l)}})),(0,t.createElement)(n.FlexBlock,null,(0,t.createElement)(n.SelectControl,{label:`Color for ${e}`,hideLabelFromVision:!0,value:i(l)?i(l).hex:l,options:R(e,l),onChange:t=>function(e,t){const l={...N,[e]:t};s({primaryColorOverrides:JSON.stringify(l)})}(e,t),__nextHasNoMarginBottom:!0})),(0,t.createElement)(n.FlexItem,null,(0,t.createElement)(n.Button,{isDestructive:!0,variant:"tertiary",size:"small",onClick:()=>function(e){const t={...N};delete t[e],s({primaryColorOverrides:JSON.stringify(t)})}(e),"aria-label":`Remove ${e}`},"✕"))))),(0,t.createElement)("div",{style:{marginTop:"16px",borderTop:"1px solid #e0e0e0",paddingTop:"12px"}},(0,t.createElement)(n.Flex,{align:"flex-end"},(0,t.createElement)(n.FlexBlock,null,(0,t.createElement)(n.TextControl,{label:"New entry label",value:V,onChange:_,placeholder:"e.g., Keynote Sessions"})),(0,t.createElement)(n.FlexItem,null,(0,t.createElement)(n.Button,{variant:"secondary",size:"compact",onClick:function(){const e=V.trim();if(!e||N.hasOwnProperty(e))return;const t=o.find(e=>!H(e.hex,null))||o[0],l={...N,[e]:t.hex};s({primaryColorOverrides:JSON.stringify(l)}),_("")},disabled:!V.trim(),style:{marginBottom:"8px"}},"Add")))))),(0,t.createElement)(n.Placeholder,{icon:"calendar-alt",label:"Sessionize Schedule Block",instructions:`Configured for API code: ${d}. Configure further settings in the block sidebar.`}))}})}},l={};function n(e){var a=l[e];if(void 0!==a)return a.exports;var o=l[e]={exports:{}};return t[e](o,o.exports,n),o.exports}n.m=t,e=[],n.O=(t,l,a,o)=>{if(!l){var i=1/0;for(c=0;c=o)&&Object.keys(n.O).every(e=>n.O[e](l[s]))?l.splice(s--,1):(r=!1,o0&&e[c-1][2]>o;c--)e[c]=e[c-1];e[c]=[l,a,o]},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={57:0,350:0};n.O.j=t=>0===e[t];var t=(t,l)=>{var a,o,[i,r,s]=l,d=0;if(i.some(t=>0!==e[t])){for(a in r)n.o(r,a)&&(n.m[a]=r[a]);if(s)var c=s(n)}for(t&&t(l);dn(285));a=n.O(a)})(); \ No newline at end of file diff --git a/web/wp-content/plugins/sessionize-blocks/blocks/sessionize-schedule/src/edit.js b/web/wp-content/plugins/sessionize-blocks/blocks/sessionize-schedule/src/edit.js index 6f09ec78..d2b4ee2c 100644 --- a/web/wp-content/plugins/sessionize-blocks/blocks/sessionize-schedule/src/edit.js +++ b/web/wp-content/plugins/sessionize-blocks/blocks/sessionize-schedule/src/edit.js @@ -1,7 +1,81 @@ import { useBlockProps, InspectorControls } from '@wordpress/block-editor'; -import { PanelBody, TextControl, TextareaControl, ToggleControl, SelectControl, Placeholder, ColorPicker, Button, Flex, FlexItem, FlexBlock } from '@wordpress/components'; +import { PanelBody, TextControl, TextareaControl, ToggleControl, SelectControl, Placeholder, Button, Flex, FlexItem, FlexBlock } from '@wordpress/components'; import { useState } from '@wordpress/element'; +/* + * Curated track colours. + * + * The front end only keeps the hue of an override: saturation is raised to at + * least 55% and lightness is fixed (95% card background, 50% border — see + * primaryColorsFromName() in view.js and sched_primary_colors() in + * includes/data.php). Colours with similar hues therefore render identically, + * so a free-form picker offers far more choices than the schedule can show. + * + * These twenty hues were spaced with a perceptual colour-difference check + * (CIEDE2000) to maximise the smallest difference between any two border + * colours (every pair differs by ~11.5 or more at 50% lightness). At the pale + * 95% card background, the closest neighbours (Teal/Aqua, Aqua/Cyan, + * Pink/Crimson, Fuchsia/Pink) are near-identical, so tracks are told apart by + * the border and chip rather than the tint. Saturation is not used as a second + * dimension: within the 55–100% range the plugin allows, a muted and a vivid + * version of one hue are too close to tell apart. Each hex is + * hsl(hue 70% 50%), matching auto-coloured tracks, and round-trips exactly. + */ +const TRACK_PALETTE = [ + { label: 'Scarlet', hue: 13, hex: '#d94d26' }, + { label: 'Vermilion', hue: 25, hex: '#d97126' }, + { label: 'Orange', hue: 35, hex: '#d98e26' }, + { label: 'Amber', hue: 45, hex: '#d9ac26' }, + { label: 'Yellow', hue: 57, hex: '#d9d026' }, + { label: 'Lime', hue: 77, hex: '#a6d926' }, + { label: 'Green', hue: 120, hex: '#26d926' }, + { label: 'Emerald', hue: 153, hex: '#26d988' }, + { label: 'Teal', hue: 171, hex: '#26d9be' }, + { label: 'Aqua', hue: 183, hex: '#26d0d9' }, + { label: 'Cyan', hue: 192, hex: '#26b5d9' }, + { label: 'Sky', hue: 202, hex: '#2697d9' }, + { label: 'Azure', hue: 213, hex: '#2677d9' }, + { label: 'Cobalt', hue: 224, hex: '#2656d9' }, + { label: 'Blue', hue: 241, hex: '#2926d9' }, + { label: 'Violet', hue: 274, hex: '#8b26d9' }, + { label: 'Orchid', hue: 295, hex: '#ca26d9' }, + { label: 'Fuchsia', hue: 321, hex: '#d9269a' }, + { label: 'Pink', hue: 339, hex: '#d92665' }, + { label: 'Crimson', hue: 353, hex: '#d9263b' }, +]; + +function paletteEntry( hex ) { + const needle = String( hex || '' ).toLowerCase(); + return TRACK_PALETTE.find( ( p ) => p.hex === needle ) || null; +} + +/* + * Preview styles that mirror how a session card is actually drawn, rather than + * showing the raw hex (which the front end never displays as-is). + */ +function swatchStyle( hex ) { + const entry = paletteEntry( hex ); + const base = { + width: '28px', + height: '28px', + borderRadius: '4px', + boxSizing: 'border-box', + flexShrink: 0, + }; + + if ( entry ) { + return { + ...base, + background: `hsl(${ entry.hue } 70% 95%)`, + border: `1px solid hsl(${ entry.hue } 70% 50%)`, + borderLeftWidth: '5px', + }; + } + + // Legacy custom colour chosen with the old picker. + return { ...base, background: hex, border: '1px solid #ccc' }; +} + export default function Edit( { attributes, setAttributes } ) { const { apiCode, publicSlug, primaryFilterTitle, timeFormat, dateFormat, @@ -27,7 +101,6 @@ export default function Edit( { attributes, setAttributes } ) { } const colorEntries = Object.entries( colorMap ); - const [ expandedColorKey, setExpandedColorKey ] = useState( null ); const [ newColorLabel, setNewColorLabel ] = useState( '' ); function updateColor( key, color ) { @@ -39,18 +112,44 @@ export default function Edit( { attributes, setAttributes } ) { const updated = { ...colorMap }; delete updated[ key ]; setAttributes( { primaryColorOverrides: JSON.stringify( updated ) } ); - if ( expandedColorKey === key ) { - setExpandedColorKey( null ); + } + + // Which track (if any) already uses each palette colour. + function trackUsing( hex, exceptKey ) { + const needle = hex.toLowerCase(); + const match = colorEntries.find( + ( [ k, v ] ) => k !== exceptKey && String( v ).toLowerCase() === needle + ); + return match ? match[ 0 ] : null; + } + + function colorOptions( key, current ) { + const options = TRACK_PALETTE.map( ( p ) => { + const usedBy = trackUsing( p.hex, key ); + return { + value: p.hex, + label: usedBy ? `${ p.label } (used by ${ usedBy })` : p.label, + }; + } ); + + // Keep a colour picked with the old free-form picker selectable, so + // opening an existing page never silently changes a track's colour. + if ( current && ! paletteEntry( current ) ) { + options.unshift( { value: current, label: `Custom (${ current })` } ); } + + return options; } function addColor() { const label = newColorLabel.trim(); if ( ! label || colorMap.hasOwnProperty( label ) ) return; - const updated = { ...colorMap, [ label ]: '#000000' }; + + // Start with the first colour no other track is using yet. + const firstFree = TRACK_PALETTE.find( ( p ) => ! trackUsing( p.hex, null ) ) || TRACK_PALETTE[ 0 ]; + const updated = { ...colorMap, [ label ]: firstFree.hex }; setAttributes( { primaryColorOverrides: JSON.stringify( updated ) } ); setNewColorLabel( '' ); - setExpandedColorKey( label ); } return ( @@ -233,29 +332,24 @@ export default function Edit( { attributes, setAttributes } ) {

- Type the exact track name as it appears in Sessionize, then click the swatch to assign a color. Tracks not listed here are auto-colored. + Type the exact track name as it appears in Sessionize, then choose a color. Tracks not listed here are auto-colored.

{ colorEntries.map( ( [ key, color ] ) => (
+
{ key }
-
) ) }