Changeset 3479303
- Timestamp:
- 03/10/2026 05:19:34 PM (3 weeks ago)
- Location:
- admin-menu-editor/trunk/customizables
- Files:
-
- 2 added
- 9 edited
-
Builders/ElementBuilderFactory.php (modified) (1 diff)
-
Builders/RadioGroupBuilder.php (modified) (1 diff)
-
Controls/ChoiceControl.php (modified) (4 diffs)
-
Controls/Control.php (modified) (1 diff)
-
Controls/RadioButtonBar.php (modified) (3 diffs)
-
Controls/RadioCardGroup.php (added)
-
Controls/RadioGroup.php (modified) (6 diffs)
-
assets/_combined-base-controls.scss (modified) (1 diff)
-
assets/_radio-card-group.scss (added)
-
assets/controls.css (modified) (1 diff)
-
assets/controls.css.map (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
admin-menu-editor/trunk/customizables/Builders/ElementBuilderFactory.php
r3474817 r3479303 117 117 public function radioGroup($idOrSetting = null) { 118 118 return new RadioGroupBuilder($this->findSettings($idOrSetting)); 119 } 120 121 /** 122 * @param Setting|null|string $idOrSetting 123 * @return RadioGroupBuilder<Controls\RadioCardGroup> 124 */ 125 public function radioCardGroup($idOrSetting = null): RadioGroupBuilder { 126 return new RadioGroupBuilder($this->findSettings($idOrSetting), [], Controls\RadioCardGroup::class); 119 127 } 120 128 -
admin-menu-editor/trunk/customizables/Builders/RadioGroupBuilder.php
r2869947 r3479303 6 6 7 7 class RadioGroupBuilder extends ControlBuilder { 8 public function __construct($settings = [], $params = [] ) {9 parent::__construct( RadioGroup::class, $settings, $params);8 public function __construct($settings = [], $params = [], string $className = RadioGroup::class) { 9 parent::__construct($className, $settings, $params); 10 10 } 11 11 12 public function choiceChild($value, $childControl) {12 public function choiceChild($value, $childControl): self { 13 13 if ( !is_string($value) ) { 14 14 //Because we use the value as an array key, it must be a string -
admin-menu-editor/trunk/customizables/Controls/ChoiceControl.php
r3400655 r3479303 3 3 namespace YahnisElsts\AdminMenuEditor\Customizable\Controls; 4 4 5 use YahnisElsts\AdminMenuEditor\Customizable\Rendering\Context; 5 6 use YahnisElsts\AdminMenuEditor\Customizable\Schemas\Enum; 6 7 use YahnisElsts\AdminMenuEditor\Customizable\Settings\EnumSetting; … … 20 21 */ 21 22 protected $options = []; 23 24 /** 25 * @var array Maps option values to controls in $this->children. Each option can have up to one child control. 26 */ 27 protected array $optionChildIndex = []; 22 28 23 29 public function __construct($settings = [], $params = [], $children = []) { … … 57 63 } 58 64 } 65 66 if ( isset($params['choiceChildren']) ) { 67 foreach ($params['choiceChildren'] as $value => $childControl) { 68 $this->children[] = $childControl; 69 $index = count($this->children) - 1; 70 $this->optionChildIndex[$value] = $index; 71 } 72 } 73 } 74 75 protected function addOptionChild($optionValue, UiElement $childControl) { 76 $this->children[] = $childControl; 77 $index = count($this->children) - 1; 78 $this->optionChildIndex[$optionValue] = $index; 79 } 80 81 protected function getOptionChild($optionValue): ?UiElement { 82 if ( !isset($this->optionChildIndex[$optionValue]) ) { 83 return null; 84 } 85 $index = $this->optionChildIndex[$optionValue]; 86 return $this->children[$index] ?? null; 87 } 88 89 protected function hasOptionChild($optionValue): bool { 90 if ( isset($this->optionChildIndex[$optionValue]) ) { 91 $index = $this->optionChildIndex[$optionValue]; 92 return isset($this->children[$index]); 93 } 94 return false; 95 } 96 97 protected function generateRadioInputFor( 98 ChoiceControlOption $option, 99 string $fieldName, 100 bool $isChecked, 101 Context $context 102 ): string { 103 return $this->buildTag( 104 'input', 105 array_merge(array( 106 'type' => 'radio', 107 'name' => $fieldName, 108 'value' => $this->mainBinding->encodeForForm($option->value), 109 'class' => $this->getInputClasses($context), 110 'checked' => $isChecked, 111 'disabled' => !$option->enabled, 112 'data-bind' => $this->makeKoDataBind([ 113 'checked' => $this->getKoObservableExpression($option->value), 114 'checkedValue' => wp_json_encode($option->value), 115 'ameObservableChangeEvents' => 'true', 116 ]), 117 ), $this->inputAttributes) 118 ); 59 119 } 60 120 … … 67 127 $this->options 68 128 ); 129 130 //Option values can be things that aren't valid JS identifiers, so we'll serialize 131 //the option-to-child relationship as an array of value + child index pairs. 132 $pairs = []; 133 foreach ($this->optionChildIndex as $value => $childIndex) { 134 $pairs[] = [$value, $childIndex]; 135 } 136 $params['valueChildIndexes'] = $pairs; 137 69 138 return $params; 70 139 } -
admin-menu-editor/trunk/customizables/Controls/Control.php
r3406324 r3479303 258 258 259 259 return $this->buildTag($tagName, $attributes, $content); 260 } 261 262 /** 263 * Generate an HTML tag that will serve as the outer container for this control. 264 * 265 * This is intended to be used for fieldset or div tags that wrap the entire control, and it 266 * will include classes and styles from the control's properties. 267 * 268 * @param array $prependClasses 269 * @param array $attributes 270 * @param string $tagName 271 * @return string 272 */ 273 protected function buildContainerElement( 274 array $prependClasses = [], 275 array $attributes = [], 276 string $tagName = 'div' 277 ): string { 278 $mergedAttributes = array_merge( 279 [ 280 'class' => array_merge($prependClasses, $this->classes), 281 'style' => $this->styles, 282 ], 283 $attributes 284 ); 285 286 return $this->buildTag($tagName, $mergedAttributes); 287 } 288 289 /** 290 * As buildContainerElement(), but specifically for fieldset tags. Adds the enabled/disabled state 291 * if applicable. 292 * 293 * @param Context $context 294 * @param array $prependClasses 295 * @param array $attributes 296 * @return string 297 */ 298 protected function buildFieldsetContainer(Context $context, array $prependClasses = [], array $attributes = []): string { 299 $attributes['disabled'] = !$this->isEnabled($context); 300 $attributes['data-bind'] = $this->makeKoDataBind($this->getKoEnableBinding()); 301 return $this->buildContainerElement($prependClasses, $attributes, 'fieldset'); 260 302 } 261 303 -
admin-menu-editor/trunk/customizables/Controls/RadioButtonBar.php
r3397404 r3479303 12 12 protected $declinesExternalLineBreaks = true; 13 13 14 protected $controlClass = 'ame-radio-button-bar-control';14 protected string $controlClass = 'ame-radio-button-bar-control'; 15 15 16 16 public function renderContent(Renderer $renderer, Context $context) { … … 19 19 20 20 //phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped 21 echo $this->buildTag( 22 'fieldset', 23 [ 24 'class' => array_merge([$this->controlClass], $this->classes), 25 'style' => $this->styles, 26 'disabled' => !$this->isEnabled($context), 27 'data-bind' => $this->makeKoDataBind($this->getKoEnableBinding()), 28 ] 29 ); 21 echo $this->buildFieldsetContainer($context, [$this->controlClass]); 30 22 foreach ($this->options as $option) { 31 23 $isChecked = ($currentValue === $option->value); … … 36 28 )); 37 29 38 echo $this->buildTag( 39 'input', 40 array_merge(array( 41 'type' => 'radio', 42 'name' => $fieldName, 43 'value' => $this->mainBinding->encodeForForm($option->value), 44 'class' => $this->getInputClasses($context), 45 'checked' => $isChecked, 46 'disabled' => !$option->enabled, 47 'data-bind' => $this->makeKoDataBind([ 48 'checked' => $this->getKoObservableExpression($option->value), 49 'checkedValue' => wp_json_encode($option->value), 50 'ameObservableChangeEvents' => 'true', 51 ]), 52 ), $this->inputAttributes) 53 ); 30 echo $this->generateRadioInputFor($option, $fieldName, $isChecked, $context); 54 31 55 32 $buttonContent = esc_html($option->label); -
admin-menu-editor/trunk/customizables/Controls/RadioGroup.php
r3471264 r3479303 24 24 protected $descriptionsAsTooltips = false; 25 25 26 /**27 * @var Control[]28 */29 protected $choiceChildren = [];30 31 26 public function __construct($settings = [], $params = [], $children = []) { 32 27 parent::__construct($settings, $params, $children); 33 28 34 if ( isset($params['choiceChildren']) ) { 35 $this->choiceChildren = $params['choiceChildren']; 36 } 37 38 $this->wrapStyle = isset($params['wrap']) ? $params['wrap'] : self::WRAP_PARAGRAPH; 29 $this->wrapStyle = $params['wrap'] ?? self::WRAP_PARAGRAPH; 39 30 switch ($this->wrapStyle) { 40 31 case self::WRAP_LINE_BREAK: … … 62 53 63 54 $classes = $this->classes; 64 $hasNestedControls = !empty($this-> choiceChildren);55 $hasNestedControls = !empty($this->optionChildIndex); 65 56 if ( $hasNestedControls ) { 66 57 $classes[] = 'ame-rg-has-nested-controls'; … … 91 82 echo $beforeOption; 92 83 $labelClasses = ['ame-rg-option-label']; 93 if ( is_string($option->value) && isset($this->choiceChildren[$option->value]) ) {84 if ( is_string($option->value) && $this->hasOptionChild($option->value) ) { 94 85 $labelClasses[] = 'ame-rg-has-choice-child'; 95 86 } … … 129 120 echo $afterOption; 130 121 131 if ( is_string($option->value) && isset($this->choiceChildren[$option->value]) ) { 132 $childControl = $this->choiceChildren[$option->value]; 133 echo HtmlHelper::tag('span', ['class' => 'ame-rg-nested-control']); 134 $renderer->renderControl($childControl, $context); 135 echo '</span>'; 122 if ( is_string($option->value) ) { 123 $child = $this->getOptionChild($option->value); 124 if ( $child instanceof Control ) { 125 echo HtmlHelper::tag('span', ['class' => 'ame-rg-nested-control']); 126 $renderer->renderControl($child, $context); 127 echo '</span>'; 128 } 136 129 } 137 130 } … … 146 139 * @return string 147 140 */ 148 protected function getRadioInputId( $option){141 protected function getRadioInputId(ChoiceControlOption $option): string { 149 142 return $this->getRadioInputPrefix() . sanitize_key(strval($option->value)); 150 143 } 151 144 152 protected function getRadioInputPrefix() {145 protected function getRadioInputPrefix(): string { 153 146 return self::INPUT_ID_PREFIX . $this->instanceNumber . '-'; 154 }155 156 public function serializeForJs(Context $context): array {157 $result = parent::serializeForJs($context);158 if ( !isset($result['children']) ) {159 $result['children'] = [];160 foreach ($this->choiceChildren as $child) {161 $result['children'][] = $child->serializeForJs($context);162 }163 }164 return $result;165 147 } 166 148 … … 168 150 $params = parent::getKoComponentParams(); 169 151 170 $hasNestedControls = !empty($this-> choiceChildren);152 $hasNestedControls = !empty($this->optionChildIndex); 171 153 $params['wrapStyle'] = $hasNestedControls ? self::WRAP_NONE : $this->wrapStyle; 172 154 $params['radioInputPrefix'] = $this->getRadioInputPrefix(); 173 155 174 if ( $hasNestedControls ) {175 //Values can be things that aren't valid JS identifiers, so we'll serialize176 //the value-to-child relationship as an array of value + child index pairs.177 $valueChildIndexes = [];178 $i = 0;179 foreach ($this->choiceChildren as $value => $child) {180 $valueChildIndexes[] = [$value, $i];181 $i++;182 }183 $params['valueChildIndexes'] = $valueChildIndexes;184 }185 186 156 return $params; 187 157 } 188 189 public function getChildren(): array {190 return array_unique(array_merge(array_values($this->choiceChildren), $this->children), SORT_REGULAR);191 }192 193 public function getAllDescendants() {194 foreach ($this->getChildren() as $child) {195 yield $child;196 if ( $child instanceof ControlContainer ) {197 yield from $child->getAllDescendants();198 }199 }200 }201 158 } -
admin-menu-editor/trunk/customizables/assets/_combined-base-controls.scss
r3405244 r3479303 8 8 @import "radio-button-bar"; 9 9 @import "radio-group"; 10 @import "radio-card-group"; -
admin-menu-editor/trunk/customizables/assets/controls.css
r3405244 r3479303 270 270 } 271 271 272 /* 273 Standard WordPress admin colors. 274 275 Announcement post: 276 https://make.wordpress.org/core/2021/02/23/standardization-of-wp-admin-colors-in-wordpress-5-7/ 277 278 Source: 279 https://codepen.io/ryelle/pen/WNGVEjw 280 281 A "wp" prefix has been added to avoid name conflicts with other code in this plugin. 282 */ 283 .ame-radio-card-group-control { 284 display: grid; 285 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); 286 grid-gap: 0.5rem; 287 } 288 289 .ame-radio-card, .form-table td fieldset label.ame-radio-card { 290 display: flex; 291 flex-direction: row; 292 align-items: flex-start; 293 gap: 0.75rem; 294 padding: 0.75rem; 295 margin: 0; 296 border: 1px solid #dcdcde; 297 border-radius: 4px; 298 background: #fff; 299 cursor: pointer; 300 transition: none; 301 } 302 .ame-radio-card:hover, .form-table td fieldset label.ame-radio-card:hover { 303 border-color: #c3c4c7; 304 } 305 .ame-radio-card:has(> .ame-radio-card-input-wrapper input[type=radio]:checked), .form-table td fieldset label.ame-radio-card:has(> .ame-radio-card-input-wrapper input[type=radio]:checked) { 306 border-color: #3582c4; 307 outline: 1px solid #3582c4; 308 outline-offset: 0; 309 } 310 311 .ame-radio-card-group-control label.ame-radio-card { 312 margin: 0 !important; 313 } 314 315 .ame-radio-card-input-wrapper { 316 flex: 0 0 auto; 317 } 318 319 .ame-radio-card-body { 320 display: flex; 321 flex-direction: column; 322 justify-content: center; 323 } 324 325 .ame-radio-card-label { 326 color: #646970; 327 } 328 272 329 /*# sourceMappingURL=controls.css.map */ -
admin-menu-editor/trunk/customizables/assets/controls.css.map
r3405244 r3479303 1 {"version":3,"sourceRoot":"","sources":["../../css/_collections.scss","../../css/_ui-constants.scss","_image-selector.scss","_code-editor.scss","../../css/_forms.scss","_number-input.scss","_popup-slider.scss","_radio-button-bar.scss","_radio-group.scss" ],"names":[],"mappings":"AAGA;EAEC,aCM4B;;ADF5B;EACC;;;AAQF;EACC,aCGwB;EDAxB;EACA;;;AAGD;EACC;IACC,aCHyB;IDOzB;;;AE/BD;EACC;EACA;EACA;EACA;EAEA;EACA;;AAEA;EACC;;AAIF;EACC;EACA;EACA;;AAGD;EACC;;AAGD;EACC;EACA;;AAGD;EACC;EACA;;;AAKF;EACC;EACA;;AAGA;EACC;EACA;EACA;EACA;;AAKD;EACC;;AAID;EACC;;AAGD;EACC;;AAKD;EACC;;;ACjED;EACC;;AAED;EACC;EACA;EACA;;;ACOA;EACC,cALY;;AAQZ;EACC;;;ACZJ;EACC,OAFiB;;;AAOlB;EACC,OARiB;;;ACLlB;EACC;;;AAGD;EAoBC;EAEA;EACA;EACA;EACA;EACA;;AAzBA;EACC;;AA2BD;EACC;EACA,QA1BY;EA2BZ;EACA;EAEA;;AAID;EACC;EACA;EACA,WArCY;EAyCZ;;AAEA;EACC;EACA;EACA;EACA;EACA;EACA,eAvCgB;;AA2ClB;EACC;EACA,OAvDY;EAwDZ,QAxDY;EA0DZ;EACA;EACA;EAEA;EAEA;EACA;;AAOD;EACC;EAEA;EACA,QALS;EAOT,eAPS;EAQT;EACA;EAEA;EACA;EACA;EAEA;;AAEA;EACC;EACA;EACA,OApBQ;EAqBR,QArBQ;EAsBR;EAEA;EAEA;EACA;;AAIF;EACC;;AAGD;EACC;EAEA;;;ACtHF;EACC;EACA;;AAEA;EHFA;EACA;EACA;EACA;EACA;;AGEA;EACC;;AAID;EACC;EACA;EACA;EACA;EACA;;AAGD;EACC;EACA;EACA;;AAGD;EACC;EACA;EACA;;AAID;EACC;EACA;EACA;EACA;EACA;;AAGD;EACC;EACA;;AAGC;EACC;;AAMF;EACC;EACA;EACA;;AAGD;EACC;EACA;EACA;;;ACjEH;EAKC;EACA;EACA;EACA;EACA;;AAEA;EACC;;AAGD;EACC;;AAGD;EACC;;;AAQF;EACC;;AAGA;EACC;;AAOD;EACC;;;AAMD;EACC;EACA;;AAEA;EACC;;AAGD;EACC;;AAOD;EACC;;AACA;EACC","file":"controls.css"}1 {"version":3,"sourceRoot":"","sources":["../../css/_collections.scss","../../css/_ui-constants.scss","_image-selector.scss","_code-editor.scss","../../css/_forms.scss","_number-input.scss","_popup-slider.scss","_radio-button-bar.scss","_radio-group.scss","../../css/_wp-admin-colors.scss","_radio-card-group.scss"],"names":[],"mappings":"AAGA;EAEC,aCM4B;;ADF5B;EACC;;;AAQF;EACC,aCGwB;EDAxB;EACA;;;AAGD;EACC;IACC,aCHyB;IDOzB;;;AE/BD;EACC;EACA;EACA;EACA;EAEA;EACA;;AAEA;EACC;;AAIF;EACC;EACA;EACA;;AAGD;EACC;;AAGD;EACC;EACA;;AAGD;EACC;EACA;;;AAKF;EACC;EACA;;AAGA;EACC;EACA;EACA;EACA;;AAKD;EACC;;AAID;EACC;;AAGD;EACC;;AAKD;EACC;;;ACjED;EACC;;AAED;EACC;EACA;EACA;;;ACOA;EACC,cALY;;AAQZ;EACC;;;ACZJ;EACC,OAFiB;;;AAOlB;EACC,OARiB;;;ACLlB;EACC;;;AAGD;EAoBC;EAEA;EACA;EACA;EACA;EACA;;AAzBA;EACC;;AA2BD;EACC;EACA,QA1BY;EA2BZ;EACA;EAEA;;AAID;EACC;EACA;EACA,WArCY;EAyCZ;;AAEA;EACC;EACA;EACA;EACA;EACA;EACA,eAvCgB;;AA2ClB;EACC;EACA,OAvDY;EAwDZ,QAxDY;EA0DZ;EACA;EACA;EAEA;EAEA;EACA;;AAOD;EACC;EAEA;EACA,QALS;EAOT,eAPS;EAQT;EACA;EAEA;EACA;EACA;EAEA;;AAEA;EACC;EACA;EACA,OApBQ;EAqBR,QArBQ;EAsBR;EAEA;EAEA;EACA;;AAIF;EACC;;AAGD;EACC;EAEA;;;ACtHF;EACC;EACA;;AAEA;EHFA;EACA;EACA;EACA;EACA;;AGEA;EACC;;AAID;EACC;EACA;EACA;EACA;EACA;;AAGD;EACC;EACA;EACA;;AAGD;EACC;EACA;EACA;;AAID;EACC;EACA;EACA;EACA;EACA;;AAGD;EACC;EACA;;AAGC;EACC;;AAMF;EACC;EACA;EACA;;AAGD;EACC;EACA;EACA;;;ACjEH;EAKC;EACA;EACA;EACA;EACA;;AAEA;EACC;;AAGD;EACC;;AAGD;EACC;;;AAQF;EACC;;AAGA;EACC;;AAOD;EACC;;;AAMD;EACC;EACA;;AAEA;EACC;;AAGD;EACC;;AAOD;EACC;;AACA;EACC;;;AClEJ;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;ACGA;EACC;EACA;EACA;;;AAGD;EACC;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;EAEA;EACA;;AAEA;EACC;;AAGD;EACC;EACA;EACA;;;AAIF;EAEC;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;;;AAOD;EACC,OTjDqB","file":"controls.css"}
Note: See TracChangeset
for help on using the changeset viewer.