feat(react): implement default and minimal video skins (#550)

This commit is contained in:
Sam Potts
2026-02-19 07:43:44 +11:00
committed by GitHub
parent b3a31a335a
commit 7d3be367f5
44 changed files with 1332 additions and 160 deletions
+26 -4
View File
@@ -1,11 +1,33 @@
import { isPlainObject, isString } from '../predicate';
type ClassValue = string | Record<string, unknown> | undefined;
/**
* A (very basic) utility to merge class names and make them a little easier to read.
* Aims to replicate the API of popular libraries like `clsx` and `classnames` but with a much simpler implementation.
* This is not intended to be a full replacement for those libraries, but it should be sufficient for our use case.
* It also allows us to avoid adding an additional dependency to our packages.
* @param classes - An array of class names, which can be strings or undefined. Undefined values will be filtered out.
* @returns A single string of class names, separated by spaces.
*
* @example
* ```ts
* cn('foo', { bar: true, baz: false }, 'qux');
* // => 'foo bar qux'
* ```
*/
export function cn(...classes: (string | undefined)[]): string {
return classes.filter(Boolean).join(' ');
export function cn(...classes: ClassValue[]): string {
const result: string[] = [];
for (const value of classes) {
if (isString(value) && value) {
result.push(value);
} else if (isPlainObject(value)) {
for (const key in value) {
if (value[key]) {
result.push(key);
}
}
}
}
return result.join(' ');
}
+28
View File
@@ -29,4 +29,32 @@ describe('cn', () => {
it('preserves class names with multiple words', () => {
expect(cn('foo bar', 'baz')).toBe('foo bar baz');
});
it('includes keys with truthy values from an object', () => {
expect(cn({ foo: true, bar: false })).toBe('foo');
});
it('includes keys for any truthy value', () => {
expect(cn({ a: 1, b: 'yes', c: 0, d: '', e: null, f: undefined, g: true })).toBe('a b g');
});
it('handles an empty object', () => {
expect(cn({})).toBe('');
});
it('handles an object where all values are falsy', () => {
expect(cn({ foo: false, bar: 0, baz: '' })).toBe('');
});
it('mixes strings and objects', () => {
expect(cn('foo', { bar: true, baz: false }, 'qux')).toBe('foo bar qux');
});
it('handles multiple objects', () => {
expect(cn({ a: true }, { b: true, c: false })).toBe('a b');
});
it('mixes strings, objects, and undefined', () => {
expect(cn('foo', undefined, { bar: true }, '', { baz: false })).toBe('foo bar');
});
});