@solid-primitives/props
Advanced tools
Comparing version 1.1.0 to 2.0.0
import { Accessor, Setter, Component, JSX } from 'solid-js'; | ||
import { Simplify, AnyObject, UnboxLazy, RequiredKeys, AnyFunction } from '@solid-primitives/utils'; | ||
declare type PropType = "boolean" | "number" | "string" | "object"; | ||
declare type PropObjectOptions<T> = T[] | Record<string, T> | any; | ||
declare type PropOptions<T> = (T extends undefined ? { | ||
declare type TestPropType = "boolean" | "number" | "string" | "object"; | ||
declare type TestPropObjectOptions<T> = T[] | Record<string, T> | any; | ||
declare type TestPropOptions<T> = (T extends undefined ? { | ||
initialValue?: T; | ||
@@ -12,7 +13,7 @@ } : { | ||
max?: T extends number ? number : undefined; | ||
options?: PropObjectOptions<T>; | ||
type?: PropType; | ||
options?: TestPropObjectOptions<T>; | ||
type?: TestPropType; | ||
}; | ||
declare type PropReturn<T> = [value: Accessor<T>, setValue: Setter<T>, field: Component]; | ||
declare type PropProps<T> = { | ||
declare type TestPropReturn<T> = [value: Accessor<T>, setValue: Setter<T>, field: Component]; | ||
declare type TestPropProps<T> = { | ||
name: string; | ||
@@ -24,6 +25,6 @@ value: Accessor<T>; | ||
}; | ||
declare const BoolProp: Component<PropProps<boolean>>; | ||
declare const NumberProp: Component<PropProps<number>>; | ||
declare const StringProp: Component<PropProps<string>>; | ||
declare const SelectProp: <T extends unknown>(props: PropProps<T> & { | ||
declare const BoolProp: Component<TestPropProps<boolean>>; | ||
declare const NumberProp: Component<TestPropProps<number>>; | ||
declare const StringProp: Component<TestPropProps<string>>; | ||
declare const SelectProp: <T extends unknown>(props: TestPropProps<T> & { | ||
options: any; | ||
@@ -38,5 +39,5 @@ }) => JSX.Element; | ||
* @example ```ts | ||
* createProp('value', 'test'); | ||
* createProp('page', { initialValue: 7, min: 1, max: 99 }); | ||
* createProp('language', { | ||
* createControlledProp('value', 'test'); | ||
* createControlledProp('page', { initialValue: 7, min: 1, max: 99 }); | ||
* createControlledProp('language', { | ||
* initialValue: 'en', | ||
@@ -48,10 +49,10 @@ * options: ['en', 'de', 'it', 'jp', 'cn', 'xy'] | ||
*/ | ||
declare function createProp<T extends undefined>(name: string, options?: PropOptions<T>): PropReturn<T>; | ||
declare function createProp<T = boolean>(name: string, options: PropOptions<T> | T): PropReturn<T>; | ||
declare function createProp<T = number>(name: string, options: PropOptions<T> | T): PropReturn<T>; | ||
declare function createProp<T = string>(name: string, options: PropOptions<T> | T): PropReturn<T>; | ||
declare function createProp<T = any>(name: string, options: PropOptions<T>): PropReturn<T>; | ||
declare function createProp<T>(name: string, options: T extends undefined ? never : T extends object ? PropOptions<T> : PropOptions<T> | T): PropReturn<T>; | ||
declare type CreateProps = <Props extends { | ||
[name: string]: boolean | number | string | PropOptions<boolean | number | string | object>; | ||
declare function createControlledProp<T extends undefined>(name: string, options?: TestPropOptions<T>): TestPropReturn<T>; | ||
declare function createControlledProp<T = boolean>(name: string, options: TestPropOptions<T> | T): TestPropReturn<T>; | ||
declare function createControlledProp<T = number>(name: string, options: TestPropOptions<T> | T): TestPropReturn<T>; | ||
declare function createControlledProp<T = string>(name: string, options: TestPropOptions<T> | T): TestPropReturn<T>; | ||
declare function createControlledProp<T = any>(name: string, options: TestPropOptions<T>): TestPropReturn<T>; | ||
declare function createControlledProp<T>(name: string, options: T extends undefined ? never : T extends object ? TestPropOptions<T> : TestPropOptions<T> | T): TestPropReturn<T>; | ||
declare type CreateTestProps = <Props extends { | ||
[name: string]: boolean | number | string | TestPropOptions<boolean | number | string | object>; | ||
}>(props: Props) => [ | ||
@@ -68,3 +69,3 @@ props: { | ||
* | ||
* @param props {Record<string, PropOptions>} | ||
* @param props {Record<string, TestPropOptions>} | ||
* @returns ```ts | ||
@@ -81,3 +82,3 @@ * [ | ||
* @example ```ts | ||
* const [props, fields] = createProps({ | ||
* const [props, fields] = createControlledProps({ | ||
* value: { initialValue: '' }, | ||
@@ -94,4 +95,60 @@ * disabled: { initialValue: false }, | ||
*/ | ||
declare const createProps: CreateProps; | ||
declare const createControlledProps: CreateTestProps; | ||
export { BoolProp, CreateProps, NumberProp, PropObjectOptions, PropOptions, PropProps, PropReturn, PropType, SelectProp, StringProp, createProp, createProps }; | ||
/** | ||
* converts inline string styles to object form | ||
* @example | ||
* const styles = stringStyleToObject("margin: 24px; border: 1px solid #121212"); | ||
* styles; // { margin: "24px", border: "1px solid #121212" } | ||
* */ | ||
declare function stringStyleToObject(style: string): JSX.CSSProperties; | ||
/** | ||
* Combines two set of styles together. Accepts both string and object styles.\ | ||
* @example | ||
* const styles = combineStyle("margin: 24px; border: 1px solid #121212", { | ||
* margin: "2rem", | ||
* padding: "16px" | ||
* }); | ||
* styles; // { margin: "2rem", border: "1px solid #121212", padding: "16px" } | ||
*/ | ||
declare function combineStyle(a: string, b: string): string; | ||
declare function combineStyle(a: JSX.CSSProperties, b: JSX.CSSProperties): JSX.CSSProperties; | ||
declare function combineStyle(a: JSX.CSSProperties | string, b: JSX.CSSProperties | string): JSX.CSSProperties; | ||
declare type PropsInput = { | ||
class?: string; | ||
className?: string; | ||
classList?: Record<string, boolean | undefined>; | ||
style?: JSX.CSSProperties | string; | ||
ref?: Element | ((el: any) => void); | ||
} & AnyObject; | ||
declare type OverrideProp<T, U, K extends keyof U> = Exclude<U[K], undefined> | (undefined extends U[K] ? (K extends keyof T ? T[K] : undefined) : never); | ||
declare type Override<T, U> = { | ||
[K in keyof Omit<T, RequiredKeys<U>>]: T[K] | Exclude<U[K & keyof U], undefined>; | ||
} & { | ||
[K in keyof Omit<U, Exclude<keyof T, RequiredKeys<U>>>]: K extends `on${infer R}` ? R extends Capitalize<R> ? K extends keyof T ? T[K] extends AnyFunction ? (...args: U[K] extends AnyFunction ? Parameters<T[K]> & Parameters<U[K]> : Parameters<T[K]>) => void : OverrideProp<T, U, K> : OverrideProp<T, U, K> : UnboxLazy<OverrideProp<T, U, K>> : UnboxLazy<OverrideProp<T, U, K>>; | ||
}; | ||
declare type MergeProps<T extends unknown[], Curr = {}> = T extends [infer Next, ...infer Rest] ? MergeProps<Rest, Next extends object ? (Next extends Function ? Curr : Override<Curr, UnboxLazy<Next>>) : Curr> : Simplify<Curr>; | ||
declare type CombineProps<T extends PropsInput[]> = Simplify<{ | ||
[K in keyof MergeProps<T>]: K extends "style" ? JSX.CSSProperties | string : MergeProps<T>[K]; | ||
}>; | ||
/** | ||
* A helper that reactively merges multiple props objects together while smartly combining some of Solid's JSX/DOM attributes. | ||
* | ||
* Event handlers and refs are chained, class, classNames and styles are combined. | ||
* For all other props, the last prop object overrides all previous ones. Similarly to {@link mergeProps} | ||
* @param sources - Multiple sets of props to combine together. | ||
* @example | ||
* ```tsx | ||
* const MyButton: Component<ButtonProps> = props => { | ||
* const { buttonProps } = createButton(); | ||
* const combined = combineProps(props, buttonProps); | ||
* return <button {...combined} /> | ||
* } | ||
* // component consumer can provide button props | ||
* // they will be combined with those provided by createButton() primitive | ||
* <MyButton style={{ margin: "24px" }} /> | ||
* ``` | ||
*/ | ||
declare function combineProps<T extends PropsInput[]>(...sources: T): CombineProps<T>; | ||
export { BoolProp, CombineProps, CreateTestProps, NumberProp, SelectProp, StringProp, TestPropObjectOptions, TestPropOptions, TestPropProps, TestPropReturn, TestPropType, combineProps, combineStyle, createControlledProp, createControlledProps, stringStyleToObject }; |
@@ -1,2 +0,19 @@ | ||
// src/index.tsx | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
// src/controlledProps.tsx | ||
import { template as _$template } from "solid-js/web"; | ||
@@ -115,3 +132,3 @@ import { createComponent as _$createComponent } from "solid-js/web"; | ||
}; | ||
function createProp(name, options) { | ||
function createControlledProp(name, options) { | ||
var _a, _b, _c; | ||
@@ -148,4 +165,4 @@ const initialValue = options == null ? void 0 : typeof options !== "object" ? options : (_b = options.initialValue) != null ? _b : defaultInitialValues[(_a = options.type) != null ? _a : "object"]; | ||
} | ||
var createProps = (props) => Object.entries(props).reduce((result, [name, options]) => { | ||
const [value, setValue, field] = createProp(name, options); | ||
var createControlledProps = (props) => Object.entries(props).reduce((result, [name, options]) => { | ||
const [value, setValue, field] = createControlledProp(name, options); | ||
result[0][name] = value; | ||
@@ -156,2 +173,66 @@ result[0][`set${name.slice(0, 1).toUpperCase()}${name.slice(1)}`] = setValue; | ||
}, [{}, []]); | ||
// src/combineProps.ts | ||
import { | ||
chain | ||
} from "@solid-primitives/utils"; | ||
import { mergeProps } from "solid-js"; | ||
var extractCSSregex = /([^:; ]*):\s*([^;\n]*)/g; | ||
function stringStyleToObject(style) { | ||
const object = {}; | ||
let match; | ||
while (match = extractCSSregex.exec(style)) { | ||
object[match[1]] = match[2]; | ||
} | ||
return object; | ||
} | ||
function combineStyle(a, b) { | ||
if (typeof a === "object" && typeof b === "object") | ||
return __spreadValues(__spreadValues({}, a), b); | ||
if (typeof a === "string" && typeof b === "string") | ||
return `${a};${b}`; | ||
const objA = typeof a === "object" ? a : stringStyleToObject(a); | ||
const objB = typeof b === "object" ? b : stringStyleToObject(b); | ||
return __spreadValues(__spreadValues({}, objA), objB); | ||
} | ||
function combineProps(...sources) { | ||
if (sources.length === 0) | ||
return {}; | ||
if (sources.length === 1) | ||
return sources[0]; | ||
const merge = mergeProps(...sources); | ||
const reduce = (key, calc) => { | ||
let v = void 0; | ||
for (const props of sources) { | ||
const propV = props[key]; | ||
if (!v) | ||
v = propV; | ||
else if (propV) | ||
v = calc(v, propV); | ||
} | ||
return v; | ||
}; | ||
return new Proxy(merge, { | ||
get(target, key) { | ||
if (typeof key !== "string") | ||
return Reflect.get(target, key); | ||
if (key === "style") | ||
return reduce("style", combineStyle); | ||
if (key === "ref" || key[0] === "o" && key[1] === "n" && key.charCodeAt(2) >= 65 && key.charCodeAt(2) <= 90) { | ||
const callbacks = []; | ||
for (const props of sources) { | ||
const cb = props[key]; | ||
if (typeof cb === "function") | ||
callbacks.push(cb); | ||
} | ||
return chain(...callbacks); | ||
} | ||
if (key === "class" || key === "className") | ||
return reduce(key, (a, b) => `${a} ${b}`); | ||
if (key === "classList") | ||
return reduce(key, (a, b) => __spreadValues(__spreadValues({}, a), b)); | ||
return Reflect.get(target, key); | ||
} | ||
}); | ||
} | ||
export { | ||
@@ -162,4 +243,7 @@ BoolProp, | ||
StringProp, | ||
createProp, | ||
createProps | ||
combineProps, | ||
combineStyle, | ||
createControlledProp, | ||
createControlledProps, | ||
stringStyleToObject | ||
}; |
{ | ||
"name": "@solid-primitives/props", | ||
"version": "1.1.0", | ||
"description": "Primitive that provides controllable props signals like knobs/controls", | ||
"version": "2.0.0", | ||
"description": "Library of primitives focused around component props.", | ||
"author": "Alex Lohr <alex.lohr@logmein.com>", | ||
"contributors": [ | ||
"Damian Tarnawski <gthetarnav@gmail.com>" | ||
], | ||
"license": "MIT", | ||
@@ -16,3 +19,4 @@ "homepage": "https://github.com/solidjs-community/solid-primitives/tree/main/packages/props", | ||
"list": [ | ||
"createProps" | ||
"combineProps", | ||
"createControlledProps" | ||
], | ||
@@ -31,2 +35,4 @@ "category": "Utilities" | ||
"scripts": { | ||
"start": "vite serve dev --host", | ||
"dev": "npm run start", | ||
"build": "tsup", | ||
@@ -42,11 +48,18 @@ "test": "uvu -r solid-register" | ||
], | ||
"dependencies": { | ||
"@solid-primitives/utils": "^1.5.0" | ||
}, | ||
"devDependencies": { | ||
"esbuild-plugin-solid": "^0.4.2", | ||
"jsdom": "^19.0.0", | ||
"nanospy": "^0.5.0", | ||
"prettier": "^2.5.1", | ||
"solid-register": "^0.1.5", | ||
"tslib": "^2.3.1", | ||
"tsup": "^5.11.11", | ||
"typescript": "^4.4.3", | ||
"uvu": "^0.5.2" | ||
"solid-register": "^0.2.5", | ||
"tslib": "^2.4.0", | ||
"tsup": "^5.12.7", | ||
"typescript": "^4.6.4", | ||
"uvu": "^0.5.3", | ||
"unocss": "0.31.13", | ||
"vite": "2.9.5", | ||
"vite-plugin-solid": "2.2.6" | ||
}, | ||
@@ -53,0 +66,0 @@ "peerDependencies": { |
@@ -12,4 +12,7 @@ <p> | ||
Creates a primitive to provide props signals for simple component testing | ||
Library of primitives focused around component props. | ||
- [`combineProps`](#combineProps) - Reactively merges multiple props objects together while smartly combining some of Solid's JSX/DOM attributes. | ||
- [`createProps`](#createProps) - Provides controllable props signals like knobs/controls for simple component testing. | ||
## Installation | ||
@@ -23,4 +26,58 @@ | ||
## How to use it | ||
## `combineProps` | ||
A helper that reactively merges multiple props objects together while smartly combining some of Solid's JSX/DOM attributes. | ||
Event handlers _(onClick, onMouseMove)_, **(every function property with name mathing `on[A-Z].\*` get's chained – lowercase like "onclick" will NOT)** and refs _(props.ref)_ are chained. | ||
`class`, `className`, `classList` and `style` are combined. | ||
For all other props, the last prop object overrides all previous ones. Similarly to Solid's [mergeProps](https://www.solidjs.com/docs/latest/api#mergeprops). | ||
### How to use it | ||
```tsx | ||
import { combineProps } from "@solid-primitives/props"; | ||
const MyButton: Component<ButtonProps> = props => { | ||
// primitives of a lot of headless ui libraries will provide props to spread | ||
const { buttonProps } = createButton(); | ||
// they can be combined with user's props easily | ||
const combined = combineProps(props, buttonProps); | ||
return <button {...combined} />; | ||
}; | ||
// component consumer can provide button props | ||
// they will be combined with those provided by createButton() primitive | ||
<MyButton style={{ margin: "24px" }} />; | ||
``` | ||
### Additional helpers | ||
A couple of lower-lever helpers that power `combineProps`: | ||
#### `stringStyleToObject` | ||
```ts | ||
const styles = stringStyleToObject("margin: 24px; border: 1px solid #121212"); | ||
styles; // { margin: "24px", border: "1px solid #121212" } | ||
``` | ||
#### `combineStyle` | ||
```ts | ||
const styles = combineStyle("margin: 24px; border: 1px solid #121212", { | ||
margin: "2rem", | ||
padding: "16px" | ||
}); | ||
styles; // { margin: "2rem", border: "1px solid #121212", padding: "16px" } | ||
``` | ||
## `createProps` | ||
Primitive that provides controllable props signals like knobs/controls for simple component testing | ||
### How to use it | ||
You can either create a single prop: | ||
@@ -30,5 +87,5 @@ | ||
// Second argument can be initialValue for boolean, number, string: | ||
const [string, setString, stringField] = createProp("stringValue", "test"); | ||
const [string, setString, stringField] = createControlledProp("stringValue", "test"); | ||
// Arrays or enums can be provided in an options object: | ||
const [language, setLanguage, languageField] = createProp( | ||
const [language, setLanguage, languageField] = createControlledProp( | ||
"language", | ||
@@ -47,3 +104,3 @@ { initialValue: "en", options: ["de", "en", "fr", "it"] as const } | ||
} | ||
const [currency, setCurrency, currencyField] = createProp("currency", { | ||
const [currency, setCurrency, currencyField] = createControlledProp("currency", { | ||
initialValue: Currency.USD, | ||
@@ -61,3 +118,3 @@ options: Currency | ||
const languages = ['de', 'en', 'fr', 'it'] as const; | ||
const [props, fields] = createProps({ | ||
const [props, fields] = createControlledProps({ | ||
boolean: true, | ||
@@ -86,3 +143,3 @@ number: 42, | ||
## Demo | ||
### Demo | ||
@@ -104,2 +161,8 @@ TODO | ||
2.0.0 - [PR#127](https://github.com/solidjs-community/solid-primitives/pull/127) | ||
Renamed `createProps` to `createControlledProps`, `createProp` to `createControlledProp` etc. (for all of the primitives focused on testing) | ||
Added `combineProps` primitive | ||
</details> |
Sorry, the diff of this file is not supported yet
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
32964
659
162
0
2
12
+ Added@solid-primitives/utils@1.5.2(transitive)