Skip to content

Commit

Permalink
chore: Improve MuxPlayer nextjs page to better account for more compl…
Browse files Browse the repository at this point in the history
…ex values. Initial re-org of prop categories.
  • Loading branch information
cjpillsbury committed Sep 9, 2024
1 parent 2aaf75a commit 18ae7e0
Show file tree
Hide file tree
Showing 3 changed files with 316 additions and 221 deletions.
30 changes: 27 additions & 3 deletions examples/nextjs-with-typescript/app/page-state/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import { Dispatch, Reducer, useEffect, useReducer } from 'react';

const DEFAULT_INITIAL_STATE = Object.freeze({}) as Readonly<Record<string, any>>;


export const isObject = (x: unknown): x is object => (x && typeof x === 'object' && !Array.isArray(x));


export const defaultToInitialState = <T extends Record<string, any> = Record<string, any>>(
query: NextParsedUrlQuery,
..._additionalArgs: any[]
Expand Down Expand Up @@ -42,10 +46,30 @@ export const reducer = <T extends Record<string, any> = Record<string, any>>(
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;
Expand Down
66 changes: 50 additions & 16 deletions examples/nextjs-with-typescript/components/renderers.tsx
Original file line number Diff line number Diff line change
@@ -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}`)}`;
Expand All @@ -19,7 +25,7 @@ export const BooleanRenderer = ({
removeFalse?: boolean;
onChange: (obj: any) => void;
}) => {
const labelStr = label ?? toWordsFromCamel(name);
const labelStr = label ?? toWordsFromKeyName(name);
return (
<div>
<label htmlFor={`${name}-control`}>
Expand All @@ -28,7 +34,10 @@ export const BooleanRenderer = ({
<input
id={`${name}-control`}
type="checkbox"
onChange={({ target: { checked } }) => onChange({ [name]: removeFalse && !checked ? undefined : checked })}
onChange={({ target: { checked } }) => {
const changeValue = removeFalse && !checked ? undefined : checked
onChange(toChangeObject(name, changeValue));
}}
checked={value ?? false}
/>
</div>
Expand All @@ -52,7 +61,7 @@ export const NumberRenderer = ({
max?: number;
step?: number;
}) => {
const labelStr = label ?? toWordsFromCamel(name);
const labelStr = label ?? toWordsFromKeyName(name);
return (
<div>
<label htmlFor={`${name}-control`}>
Expand All @@ -64,7 +73,10 @@ export const NumberRenderer = ({
min={min}
max={max}
step={step}
onChange={({ target: { value } }) => onChange({ [name]: value ? +value : undefined })}
onChange={({ target: { value } }) => {
const changeValue = value ? +value : undefined
onChange(toChangeObject(name, changeValue));
}}
value={value ?? ''}
/>
</div>
Expand All @@ -84,7 +96,7 @@ export const TextRenderer = ({
onChange: (obj: any) => void;
placeholder?: string;
}) => {
const labelStr = label ?? toWordsFromCamel(name);
const labelStr = label ?? toWordsFromKeyName(name);
return (
<div>
<label htmlFor={`${name}-control`}>
Expand All @@ -93,7 +105,10 @@ export const TextRenderer = ({
<input
id={`${name}-control`}
type="text"
onChange={({ target: { value } }) => onChange({ [name]: value ? value : undefined })}
onChange={({ target: { value } }) => {
const changeValue = value ? value : undefined;
onChange(toChangeObject(name, changeValue));
}}
value={value ?? ''}
placeholder={placeholder}
/>
Expand All @@ -114,7 +129,7 @@ export const URLRenderer = ({
onChange: (obj: any) => void;
placeholder?: string;
}) => {
const labelStr = label ?? toWordsFromCamel(name);
const labelStr = label ?? toWordsFromKeyName(name);
return (
<div>
<label htmlFor={`${name}-control`}>
Expand All @@ -123,7 +138,10 @@ export const URLRenderer = ({
<input
id={`${name}-control`}
type="url"
onChange={({ target: { value } }) => onChange({ [name]: value ? value : undefined })}
onChange={({ target: { value } }) => {
const changeValue = value ? value : undefined;
onChange(toChangeObject(name, changeValue));
}}
value={value ?? ''}
placeholder={placeholder}
/>
Expand All @@ -142,7 +160,7 @@ export const ColorRenderer = ({
label?: string;
onChange: (obj: any) => void;
}) => {
const labelStr = label ?? toWordsFromCamel(name);
const labelStr = label ?? toWordsFromKeyName(name);
return (
<div>
<label htmlFor={`${name}-control`}>
Expand All @@ -151,7 +169,10 @@ export const ColorRenderer = ({
<input
id={`${name}-control`}
type="color"
onChange={({ target: { value } }) => onChange({ [name]: value ? value : undefined })}
onChange={({ target: { value } }) => {
const changeValue = value ? value : undefined;
onChange(toChangeObject(name, changeValue));
}}
value={value ?? '#000000'}
/>
</div>
Expand Down Expand Up @@ -182,7 +203,7 @@ export const EnumRenderer = ({
values: any[];
formatter?: (enumValue: any) => ReactNode;
}) => {
const labelStr = label ?? toWordsFromCamel(name);
const labelStr = label ?? toWordsFromKeyName(name);
return (
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }}>
<label>
Expand All @@ -203,7 +224,10 @@ export const EnumRenderer = ({
<input
id={`${name}-${enumValue}-control`}
type="radio"
onChange={() => onChange({ [name]: values[i] })}
onChange={() => {
const changeValue = values[i];
onChange(toChangeObject(name, changeValue));
}}
value={typeof enumValue === 'string' ? enumValue : enumValue?.toString()}
checked={value === enumValue}
/>
Expand All @@ -229,7 +253,7 @@ export const EnumMultiSelectRenderer = ({
onChange: (obj: any) => void;
values: any[];
}) => {
const labelStr = label ?? toWordsFromCamel(name);
const labelStr = label ?? toWordsFromKeyName(name);
return (
<div>
<label htmlFor={`${name}-control`}>
Expand All @@ -241,10 +265,10 @@ export const EnumMultiSelectRenderer = ({
size={values.length}
defaultValue={value ?? []}
onChange={({ target: { selectedOptions } }) => {
const currentValues = selectedOptions?.length
const changeValue =selectedOptions?.length
? Array.from(selectedOptions, ({ value }) => values.find((enumValue) => enumValue.toString() === value))
: undefined;
onChange({ [name]: currentValues });
onChange(toChangeObject(name, changeValue));
}}
>
{values.map((enumValue) => {
Expand All @@ -258,3 +282,13 @@ export const EnumMultiSelectRenderer = ({
</div>
);
};

// NOTE: Placing this function at the bottom bc generic syntax messes up code highlighting in VSCode. This at least keeps the jank narrow. (CJP)
export const toChangeObject = <T = undefined>(name: string, value: T) => {
// NOTE: Currently only support depth=1
if (name.includes('.')) {
const [name1, name2] = name.split('.');
return { [name1]: { [name2]: value } };
}
return { [name]: value };
};
Loading

0 comments on commit 18ae7e0

Please sign in to comment.