| @@ -99,8 +99,27 @@ | ||
| 99 | 99 | * paints. Declaring it here is what |
| 100 | 100 | * keeps it off the boot critical |
| 101 | 101 | * path: it travels with the window |
| 102 | 102 | * it extends. Default empty. |
| 103 | + * @type string[] $styles Companion style handles injected on | |
| 104 | + * the window's first open, after the | |
| 105 | + * window's own `$style`, in the order | |
| 106 | + * given — so at equal specificity a | |
| 107 | + * companion's overrides win, the same | |
| 108 | + * source-order contract an enqueue | |
| 109 | + * dependency gives. The styles-side | |
| 110 | + * mirror of `$scripts`: a stylesheet | |
| 111 | + * that only paints surfaces inside | |
| 112 | + * this window is dead weight on every | |
| 113 | + * document that never shows it — | |
| 114 | + * declared here it costs nothing at | |
| 115 | + * boot and never reaches chromeless | |
| 116 | + * iframes at all. Unlike `$style` | |
| 117 | + * (injected when the window registers, | |
| 118 | + * so mid-session activations paint), | |
| 119 | + * companions wait for the first open; | |
| 120 | + * the deferral is the point. Default | |
| 121 | + * empty. | |
| 103 | 122 | * @type bool $preload_script Load `$script` (and `$scripts`) at |
| 104 | 123 | * shell boot instead of on first |
| 105 | 124 | * open. Default false — a window's |
| 106 | 125 | * bundle is dead weight until the |
| @@ -129,8 +148,15 @@ | ||
| 129 | 148 | * A PROPOSED default only: the user's |
| 130 | 149 | * OpenStation Preferences → Navigation |
| 131 | 150 | * pick wins, and so does a right-click |
| 132 | 151 | * "Keep in dock". |
| 152 | + * @type string $admin 'site' | 'network' | 'any'. Default | |
| 153 | + * 'site': offered on every site's | |
| 154 | + * shell and never on the network | |
| 155 | + * admin's, which is right for a window | |
| 156 | + * that reads the current site's REST | |
| 157 | + * API. 'network' is the network | |
| 158 | + * admin's shell only; 'any' is both. | |
| 133 | 159 | * @type string $nav_kind 'app' | 'control'. Default 'app'. |
| 134 | 160 | * What the window IS, which decides |
| 135 | 161 | * where its launcher defaults to (apps |
| 136 | 162 | * to the desktop, controls to the |
| @@ -214,8 +240,9 @@ | ||
| 214 | 240 | 'icon' => 'dashicons-admin-generic', |
| 215 | 241 | 'template' => null, |
| 216 | 242 | 'script' => '', |
| 217 | 243 | 'scripts' => array(), |
| 244 | + 'styles' => array(), | |
| 218 | 245 | 'preload_script' => false, |
| 219 | 246 | // Optional WP style handle (registered with `wp_register_style()`). |
| 220 | 247 | // Resolved at payload-build time so the shell can lazy-inject a |
| 221 | 248 | // `<link rel="stylesheet">` when a peer plugin is activated |
| @@ -227,8 +254,9 @@ | ||
| 227 | 254 | 'height' => 400, |
| 228 | 255 | 'min_width' => 280, |
| 229 | 256 | 'min_height' => 220, |
| 230 | 257 | 'placement' => 'dock', |
| 258 | + 'admin' => 'site', | |
| 231 | 259 | 'nav_kind' => 'app', |
| 232 | 260 | 'dock_order' => 0, |
| 233 | 261 | 'placeable' => false, |
| 234 | 262 | 'capabilities' => array(), |
| @@ -237,8 +265,11 @@ | ||
| 237 | 265 | 'main_tab_padding' => '', |
| 238 | 266 | 'config' => array(), |
| 239 | 267 | ); |
| 240 | 268 | $args = wp_parse_args( $args, $defaults ); |
| 269 | + if ( ! in_array( $args['admin'], array( 'site', 'network', 'any' ), true ) ) { | |
| 270 | + $args['admin'] = 'site'; | |
| 271 | + } | |
| 241 | 272 | |
| 242 | 273 | // Capability gate — ALL listed caps must match. Fail closed. |
| 243 | 274 | foreach ( (array) $args['capabilities'] as $cap ) { |
| 244 | 275 | if ( ! current_user_can( (string) $cap ) ) { |
| @@ -303,8 +334,19 @@ | ||
| 303 | 334 | } |
| 304 | 335 | ) |
| 305 | 336 | ) |
| 306 | 337 | ), |
| 338 | + // Companion style handles, same dedupe/strip as `scripts`. | |
| 339 | + 'styles' => array_values( | |
| 340 | + array_unique( | |
| 341 | + array_filter( | |
| 342 | + array_map( 'strval', (array) $args['styles'] ), | |
| 343 | + static function ( $handle ) { | |
| 344 | + return '' !== $handle; | |
| 345 | + } | |
| 346 | + ) | |
| 347 | + ) | |
| 348 | + ), | |
| 307 | 349 | 'preload_script' => (bool) $args['preload_script'], |
| 308 | 350 | 'style' => (string) $args['style'], |
| 309 | 351 | 'width' => (int) $args['width'], |
| 310 | 352 | 'height' => (int) $args['height'], |
| @@ -311,8 +353,10 @@ | ||
| 311 | 353 | 'min_width' => (int) $args['min_width'], |
| 312 | 354 | 'min_height' => (int) $args['min_height'], |
| 313 | 355 | 'placement' => $placement, |
| 314 | 356 | 'nav_kind' => $nav_kind, |
| 357 | + // Which admin's shell offers it; see the `admin` arg. | |
| 358 | + 'admin' => $args['admin'], | |
| 315 | 359 | // Sort key among system tiles, ascending. `0` (the default) |
| 316 | 360 | // puts a plugin's tile ahead of the shell's own trailing |
| 317 | 361 | // cluster — Mio 10, Overview 20, System 30 — which is where a |
| 318 | 362 | // launcher belongs. Trash uses 40 to sit at the very end. |
| @@ -486,8 +530,9 @@ | ||
| 486 | 530 | 'bordered' => true, |
| 487 | 531 | 'compact' => true, |
| 488 | 532 | 'loading' => true, |
| 489 | 533 | 'loading-rows' => true, |
| 534 | + 'empty' => true, | |
| 490 | 535 | 'columns' => true, |
| 491 | 536 | 'rows' => true, |
| 492 | 537 | 'sortable' => true, |
| 493 | 538 | 'expandable' => true, |
| @@ -492,8 +537,9 @@ | ||
| 492 | 537 | 'sortable' => true, |
| 493 | 538 | 'expandable' => true, |
| 494 | 539 | 'preset' => true, |
| 495 | 540 | 'label' => true, |
| 541 | + 'heading' => true, | |
| 496 | 542 | 'description' => true, |
| 497 | 543 | 'orientation' => true, |
| 498 | 544 | 'level' => true, |
| 499 | 545 | 'collapsed' => true, |
| @@ -931,9 +977,9 @@ | ||
| 931 | 977 | * builds that payload at 10, and data attached after it would ship a |
| 932 | 978 | * bundle with no config. |
| 933 | 979 | */ |
| 934 | 980 | function openstation_enqueue_native_window_scripts() { |
| 935 | - if ( ! openstation_is_enabled() || openstation_is_chromeless_request() || openstation_is_classic_request() ) { | |
| 981 | + if ( ! openstation_is_shell_request() ) { | |
| 936 | 982 | return; |
| 937 | 983 | } |
| 938 | 984 | $registry = openstation_native_window_registry(); |
| 939 | 985 | if ( ! is_array( $registry ) ) { |
| @@ -961,8 +1007,16 @@ | ||
| 961 | 1007 | wp_enqueue_script( $entry['script'] ); |
| 962 | 1008 | foreach ( (array) $entry['scripts'] as $companion ) { |
| 963 | 1009 | wp_enqueue_script( $companion ); |
| 964 | 1010 | } |
| 1011 | + // Preload means "everything at boot" — companion styles | |
| 1012 | + // ride along so the window paints styled on a preloaded | |
| 1013 | + // first open, same as its scripts are already parsed. | |
| 1014 | + if ( ! empty( $entry['styles'] ) ) { | |
| 1015 | + foreach ( (array) $entry['styles'] as $companion_style ) { | |
| 1016 | + wp_enqueue_style( $companion_style ); | |
| 1017 | + } | |
| 1018 | + } | |
| 965 | 1019 | } |
| 966 | 1020 | // Localize the config the JS side reads to register itself. |
| 967 | 1021 | wp_localize_script( |
| 968 | 1022 | $entry['script'], |
| @@ -1023,9 +1077,9 @@ | ||
| 1023 | 1077 | * these via `document.getElementById( `os-native-window-${id}` )` |
| 1024 | 1078 | * and clones them into each opened window's body. |
| 1025 | 1079 | */ |
| 1026 | 1080 | function openstation_render_native_window_templates() { |
| 1027 | - if ( ! openstation_is_enabled() || openstation_is_chromeless_request() || openstation_is_classic_request() ) { | |
| 1081 | + if ( ! openstation_is_shell_request() ) { | |
| 1028 | 1082 | return; |
| 1029 | 1083 | } |
| 1030 | 1084 | $registry = openstation_native_window_registry(); |
| 1031 | 1085 | if ( ! is_array( $registry ) ) { |
| @@ -1051,4 +1105,21 @@ | ||
| 1051 | 1105 | echo '</template>'; |
| 1052 | 1106 | } |
| 1053 | 1107 | } |
| 1054 | 1108 | add_action( 'admin_footer', 'openstation_render_native_window_templates', 20 ); |
| 1109 | + | |
| 1110 | +/** | |
| 1111 | + * Whether a registered window is offered on the admin this request is | |
| 1112 | + * in: the network admin's shell offers `network` and `any` windows, | |
| 1113 | + * every site's shell offers `site` and `any`. See the `admin` arg of | |
| 1114 | + * {@see openstation_register_window()}. | |
| 1115 | + * | |
| 1116 | + * @param array<string,mixed> $entry Registry entry. | |
| 1117 | + * @return bool | |
| 1118 | + */ | |
| 1119 | +function openstation_native_window_offered_here( $entry ) { | |
| 1120 | + $admin = isset( $entry['admin'] ) ? (string) $entry['admin'] : 'site'; | |
| 1121 | + if ( 'any' === $admin ) { | |
| 1122 | + return true; | |
| 1123 | + } | |
| 1124 | + return is_network_admin() ? 'network' === $admin : 'site' === $admin; | |
| 1125 | +} | |