__( 'Dashbread: App sidebar', 'dashbread' ), self::LOC_HEADER => __( 'Dashbread: App header', 'dashbread' ), self::LOC_ACCOUNT => __( 'Dashbread: Account menu', 'dashbread' ), ) ); } /** Per-member widget state: usermeta dashbread_widgets = { collapsed: [ids], hidden: [ids] }. @return array{collapsed:string[],hidden:string[]} */ public static function widget_state( int $user_id = 0 ): array { $m = get_user_meta( $user_id ?: get_current_user_id(), 'dashbread_widgets', true ); $m = is_array( $m ) ? $m : array(); return array( 'collapsed' => array_map( 'strval', (array) ( $m['collapsed'] ?? array() ) ), 'hidden' => array_map( 'strval', (array) ( $m['hidden'] ?? array() ) ) ); } /** * Dashboard widgets get a stable id, the member's collapsed/hidden state as classes (no flicker), * and the collapse / hide tools in their header. Pro's canvas adds order and column span on top. */ public function widget_params( array $params ): array { if ( ! isset( $params[0]['id'] ) || self::SIDEBAR_ID !== $params[0]['id'] ) { return $params; } $id = (string) $params[0]['widget_id']; $state = self::widget_state(); $cls = ( in_array( $id, $state['collapsed'], true ) ? ' is-collapsed' : '' ) . ( in_array( $id, $state['hidden'], true ) ? ' is-hidden' : '' ); /** @param string $classes Extra classes for this dashboard widget (Pro adds sui-span2/3). @param string $id */ $cls .= ' ' . trim( (string) apply_filters( 'dashbread_widget_classes', '', $id ) ); $params[0]['before_widget'] = str_replace( 'class="sui-card sui-widget ', 'data-sui-widget="' . esc_attr( $id ) . '" class="sui-card sui-widget' . esc_attr( rtrim( $cls ) ) . ' ', (string) $params[0]['before_widget'] ); $params[0]['after_title'] = '' . self::widget_tools( $id ) . ''; return $params; } /** The tools cluster in a dashboard widget header. Block widgets have no title markup, so shell.js builds a header and clones these from a