From 3e5f99ea5fe3a60c345a57236a0881b08fd53612 Mon Sep 17 00:00:00 2001 From: Christian Pillsbury Date: Mon, 9 Sep 2024 13:14:10 -0700 Subject: [PATCH] chore: Improve MuxPlayer nextjs page to better account for more complex values. Initial re-org of prop categories. --- .../app/page-state/index.ts | 28 +- .../components/renderers.tsx | 66 ++++- .../pages/MuxPlayer.tsx | 276 ++++++++++-------- 3 files changed, 227 insertions(+), 143 deletions(-) diff --git a/examples/nextjs-with-typescript/app/page-state/index.ts b/examples/nextjs-with-typescript/app/page-state/index.ts index 0ed7a208f..b293240fd 100644 --- a/examples/nextjs-with-typescript/app/page-state/index.ts +++ b/examples/nextjs-with-typescript/app/page-state/index.ts @@ -5,6 +5,8 @@ import { Dispatch, Reducer, useEffect, useReducer } from 'react'; const DEFAULT_INITIAL_STATE = Object.freeze({}) as Readonly>; +export const isObject = (x: unknown): x is object => (x && typeof x === 'object' && !Array.isArray(x)); + export const defaultToInitialState = = Record>( query: NextParsedUrlQuery, ..._additionalArgs: any[] @@ -42,10 +44,30 @@ export const reducer = = Record>( const { type, value } = action; switch (type) { case ActionType.UPDATE: { - return { - ...state, - ...value, + const updateFn = typeof value === 'function' ? value : (prevState) => { + return Object.entries(value).reduce((nextState, [name, value]) => { + let nextValue = value; + if (isObject(value) && isObject(nextState[name])) { + // nextValue = { ...nextState[name], ...value }; + nextValue = Object.entries(value).reduce((nextValue, [newValueKey, newValueValue]) => { + // Treat undefined (but not null) as a removal/delete condition + if (newValueValue === undefined) { + delete nextValue[newValueKey]; + } else { + nextValue[newValueKey] = newValueValue; + } + return nextValue; + }, { ...nextState[name] }); + // Also remove empty objects (aka no remaining keys after prior logic) + if (!Object.keys(nextValue).length) { + nextValue = undefined; + } + } + nextState[name] = nextValue; + return nextState; + }, { ...prevState }); }; + return updateFn(state); } default: { return state; diff --git a/examples/nextjs-with-typescript/components/renderers.tsx b/examples/nextjs-with-typescript/components/renderers.tsx index 8800828f9..6bde9950d 100644 --- a/examples/nextjs-with-typescript/components/renderers.tsx +++ b/examples/nextjs-with-typescript/components/renderers.tsx @@ -1,6 +1,12 @@ import { Fragment, ReactNode } from 'react'; -export const toWordsFromCamel = (string: string) => { +export const toWordsFromKeyName = (string: string) => { + if (string.includes('.')) { + return string.split('.').map(toWordsFromKeyName).join(': '); + } + if (string.includes('_')) { + return string.split('_').map(toWordsFromKeyName).join(' '); + } const first = string[0].toUpperCase(); const rest = string.slice(1); return `${first}${rest.replace(/[A-Z]/g, (match) => ` ${match}`)}`; @@ -19,7 +25,7 @@ export const BooleanRenderer = ({ removeFalse?: boolean; onChange: (obj: any) => void; }) => { - const labelStr = label ?? toWordsFromCamel(name); + const labelStr = label ?? toWordsFromKeyName(name); return (
@@ -52,7 +61,7 @@ export const NumberRenderer = ({ max?: number; step?: number; }) => { - const labelStr = label ?? toWordsFromCamel(name); + const labelStr = label ?? toWordsFromKeyName(name); return (
@@ -84,7 +96,7 @@ export const TextRenderer = ({ onChange: (obj: any) => void; placeholder?: string; }) => { - const labelStr = label ?? toWordsFromCamel(name); + const labelStr = label ?? toWordsFromKeyName(name); return (