> */ function tlig_nav_fields(): array { return [ 'panel' => [ 'type' => 'checkbox', 'label' => __( 'Opens a mega panel', 'tlig' ), 'help' => __( 'Top-level items only. The panel is as tall as its content.', 'tlig' ), ], 'heading' => [ 'type' => 'checkbox', 'label' => __( 'Heading, not a link', 'tlig' ), ], 'span' => [ 'type' => 'select', 'label' => __( 'Column width', 'tlig' ), 'options' => [ '' => __( 'Automatic', 'tlig' ), '3' => __( 'Quarter', 'tlig' ), '4' => __( 'Third', 'tlig' ), '6' => __( 'Half', 'tlig' ), '8' => __( 'Two thirds', 'tlig' ), '12' => __( 'Full width', 'tlig' ), ], ], 'layout' => [ 'type' => 'select', 'label' => __( 'Image position', 'tlig' ), 'options' => [ '' => __( 'No image', 'tlig' ), 'image_left' => __( 'Beside the label', 'tlig' ), 'image_below' => __( 'Above the label', 'tlig' ), ], ], 'image' => [ 'type' => 'number', 'label' => __( 'Image (attachment ID)', 'tlig' ), ], 'icon' => [ 'type' => 'select', 'label' => __( 'Icon', 'tlig' ), 'options' => [ '' => __( 'None', 'tlig' ), 'search' => __( 'Search', 'tlig' ), 'book' => __( 'Book', 'tlig' ), 'headphones' => __( 'Headphones', 'tlig' ), ], ], 'feed' => [ 'type' => 'number', 'label' => __( 'List children of page ID', 'tlig' ), 'help' => __( 'Generated at render time, so new pages appear by themselves.', 'tlig' ), ], 'block' => [ 'type' => 'textarea', 'label' => __( 'HTML block', 'tlig' ), 'help' => __( 'An escape hatch. Filtered through wp_kses_post, so it cannot carry a stylesheet.', 'tlig' ), ], ]; } /** * Draws the fields inside the menu item. * * @param int $item_id Menu item post ID. */ function tlig_nav_item_fields( $item_id ): void { $nav = tlig_nav_meta( (int) $item_id ); wp_nonce_field( 'tlig_nav_' . $item_id, 'tlig_nav_nonce_' . $item_id ); echo '
'; } add_action( 'wp_nav_menu_item_custom_fields', 'tlig_nav_item_fields', 10, 1 ); /** * Saves them. * * Stored as one array under `_tlig_nav` rather than eight meta rows: the whole * point is that an item's configuration is small enough to read at a glance. * An empty configuration deletes the row instead of writing an empty array, so * the 214 items that need no settings carry no meta at all. * * @param int $menu_id Menu term ID. * @param int $item_id Menu item post ID. */ function tlig_nav_item_save( $menu_id, $item_id ): void { $nonce = $_POST[ 'tlig_nav_nonce_' . $item_id ] ?? ''; if ( ! wp_verify_nonce( sanitize_key( $nonce ), 'tlig_nav_' . $item_id ) ) { return; } $raw = $_POST['tlig_nav'][ $item_id ] ?? []; if ( ! is_array( $raw ) ) { $raw = []; } $nav = []; foreach ( tlig_nav_fields() as $key => $field ) { $value = $raw[ $key ] ?? ''; switch ( $field['type'] ) { case 'checkbox': if ( ! empty( $value ) ) { $nav[ $key ] = 1; } break; case 'select': $value = (string) $value; if ( '' !== $value && isset( $field['options'][ $value ] ) ) { $nav[ $key ] = 'span' === $key ? (int) $value : $value; } break; case 'textarea': $value = wp_kses_post( (string) $value ); if ( '' !== trim( $value ) ) { $nav[ $key ] = $value; } break; default: $value = (int) $value; if ( $value > 0 ) { $nav[ $key ] = 'feed' === $key ? [ 'parent' => $value, 'count' => -1, 'orderby' => 'title' ] : $value; } } } if ( $nav ) { update_post_meta( $item_id, '_tlig_nav', $nav ); } else { delete_post_meta( $item_id, '_tlig_nav' ); } } add_action( 'wp_update_nav_menu_item', 'tlig_nav_item_save', 10, 2 );