@lightningjs/solid
Advanced tools
Comparing version 0.12.7 to 0.12.8
@@ -659,11 +659,16 @@ import { createSignal, mergeProps as mergeProps$1, createRoot, createRenderEffect, createMemo, createComponent as createComponent$1, untrack, onMount } from 'solid-js'; | ||
} | ||
set style(value) { | ||
set style(values) { | ||
const passedArray = isArray(values); | ||
const styleArray = passedArray ? values : [values]; | ||
// Keys set in JSX are more important | ||
for (const key in value) { | ||
// be careful of 0 values | ||
if (this[key] === undefined) { | ||
this[key] = value[key]; | ||
styleArray.forEach(value => { | ||
for (const key in value) { | ||
// be careful of 0 values | ||
if (this[key] === undefined) { | ||
this[key] = value[key]; | ||
} | ||
} | ||
} | ||
this._style = value; | ||
}); | ||
// reverse the array so the first style is the most important | ||
this._style = passedArray ? Object.assign({}, ...styleArray.reverse()) : values; | ||
} | ||
@@ -670,0 +675,0 @@ get style() { |
@@ -324,11 +324,16 @@ /* | ||
} | ||
set style(value) { | ||
set style(values) { | ||
const passedArray = isArray(values); | ||
const styleArray = passedArray ? values : [values]; | ||
// Keys set in JSX are more important | ||
for (const key in value) { | ||
// be careful of 0 values | ||
if (this[key] === undefined) { | ||
this[key] = value[key]; | ||
styleArray.forEach((value) => { | ||
for (const key in value) { | ||
// be careful of 0 values | ||
if (this[key] === undefined) { | ||
this[key] = value[key]; | ||
} | ||
} | ||
} | ||
this._style = value; | ||
}); | ||
// reverse the array so the first style is the most important | ||
this._style = (passedArray ? Object.assign({}, ...styleArray.reverse()) : values); | ||
} | ||
@@ -335,0 +340,0 @@ get style() { |
@@ -83,3 +83,3 @@ import { createShader } from '../renderer/index.js'; | ||
destroy(): void; | ||
set style(value: SolidStyles); | ||
set style(values: SolidStyles | SolidStyles[]); | ||
get style(): SolidStyles; | ||
@@ -86,0 +86,0 @@ get hasChildren(): boolean; |
{ | ||
"name": "@lightningjs/solid", | ||
"version": "0.12.7", | ||
"version": "0.12.8", | ||
"description": "Lightning renderer for solid universal", | ||
@@ -5,0 +5,0 @@ "type": "module", |
406
README.md
@@ -9,2 +9,6 @@ <p> | ||
## Documentation | ||
[SolidJS Lightning Docs](https://lightning-js.github.io/solid/) | ||
## Quick Start | ||
@@ -21,6 +25,2 @@ | ||
# Usage | ||
Most of the things you do with Solid will carry over to using Solid-Lightning with some key differences as Lightning does not use HTML / DOM / CSS / Mouse Input. | ||
### Hello World | ||
@@ -39,399 +39,1 @@ | ||
For a more detailed Hello World guide check out the [Hello World](HelloWorld.md) guide. | ||
## Built In Components | ||
### Canvas | ||
The <Canvas> element boots up the Lightning Renderer. This should be the first component passed into the render function. It takes an `options` param which is passed to the Lightning Renderer. | ||
### View and Text | ||
Everything is built with two primitive components: `<View>` and `<Text>`. Think of `<View>` like div tag for HTML, all encompassing. Whenever you want to display text, wrap it in a `<Text>` tag like so `<Text>Hello World</Text>` | ||
```jsx | ||
import { View, Text } from '@lightningjs/solid'; | ||
<View style={OverviewContainer}> | ||
<Text style={Title}>Hello World!</Text> | ||
</View>; | ||
``` | ||
## Focus / activeElement | ||
activeElement is a global Solid Signal. At any time there is one element that can be the activeElement. You can also setActiveElement at any time to any element. | ||
```jsx | ||
import { createEffect, on } from "solid-js"; | ||
import { activeElement, setActiveElement } from "@lightningjs/solid"; | ||
// Get notified whenever the activeElement changes | ||
createEffect(on(activeElement, (elm) => { | ||
focusRingRef.x = elm.x; | ||
}, { defer: true})) | ||
// autofocus will setActiveElement on this when intially created | ||
<Button autofocus>TV Shows</Button> | ||
let myButton; | ||
onMount(() => { | ||
setActiveElement(myButton) | ||
//or | ||
myButton.setFocus(); | ||
}) | ||
<Button ref={myButton}>Sports</Button> | ||
``` | ||
## Styling / Properties | ||
You can add styles to your JSX components using object notation or applying the properties directly to the JSX or via a ref: | ||
```jsx | ||
import { createEffect, createSignal } from 'solid-js'; | ||
let columnRef; | ||
const Column = { | ||
display: 'flex', | ||
flexDirection: 'column', | ||
justifyContent: 'flexStart', | ||
width: 1760, | ||
height: 500, | ||
gap: 50, | ||
y: 200, | ||
x: 80, | ||
color: '00000000', | ||
}; | ||
createEffect(() => { | ||
columnRef.x = 200; | ||
}); | ||
const [alpha, setAlpha] = createSignal(1); | ||
<View ref={columnRef} alpha={alpha()} y={90} style={Column}> | ||
// ...add some children | ||
</View>; | ||
``` | ||
The style attribute takes an object of properties and passes them to the Lightning Renderer on initial creation of the component. The style object will not be reapplied if it is changed after creation. This keeps the style object as Read Only in the templating system allowing you to use it for multiple components. Additionally, when the style object is applied any properties on the JSX will have greater precedent so you can override styles on individual components. After the component is created, you can further change props via signals or imperatively with the ref to the component. | ||
### Prop Defaults | ||
`<View>` components without a width and height value will inherit their parents width and height minus there x and y values. X and y will default to 0, 0 if not specified. `<Text>` component does not require any properties. If `<Text>` component is loaded in a flex container, it will update it's width and height when it loads. | ||
### Colors | ||
RGBA number 0xRRGGBBAA. If you want to use hex, `import { hexColor } from '@lightningjs/solid'` and do `hexColor('#c0ffee')` to convert colors to RGBA. Please know all hex colors are #RRGGBB so they are easy to convert to 0xRRGGBBAA and usually AA is ff for full alpha. By default, every node without a src attribute will have their color set to `0x00000000` making it transparent. If you have an element which sets it's src attribute after creation, you need to update color to `0xffffffff` so it's not transparent. | ||
### Border and borderRadius | ||
`border` and `borderRadius` are special props which create effects for the DynamicShader found in the Lightning Renderer. These props can be set on the JSX or style object. The order in which you set the props determine how they are applied in the shader. Meaning you probably want to set borderRadius first. You can also set individual borders via `borderLeft`, `borderRight`, `borderTop`, `borderBottom`. These properties do not support animations. | ||
``` | ||
const style = { | ||
borderRadius: 30, | ||
border: { width: 10, color: 0x000000ff } | ||
} | ||
// or | ||
const style = { | ||
borderLeft: { width: 10, color: 0x000000ff }, | ||
borderRight: { width: 10, color: 0x000000ff } | ||
} | ||
``` | ||
### linearGradient | ||
`linearGradient` is another special effect that can be used like a style with following syntax. | ||
``` | ||
linearGradient: | ||
{ | ||
angle: 225, | ||
stops: [0.1, 0.5], | ||
colors: [ | ||
0xff0000ff, 0x00000000, | ||
], | ||
}, | ||
``` | ||
You can have as many stops or colors as you like. | ||
## Layout | ||
When a child element changes size updateLayout method on the node will be called. You can use `onBeforeLayout` and `onLayout` hooks to update the element with the following signature `(node, { width, height})`. You can use this callback to resize the parent node before flex is calculated using `onBeforeLayout` and after flex with `onLayout`. If you do, call `parent.updateLayout` for it to also resize. | ||
### Flex | ||
At the moment there is a very barebone flex implementation (`display: flex`). It only supports `flexDirection`, `justifyContent`, `alignItems` and `gap` at the moment. But very useful for laying out rows and columns. | ||
```jsx | ||
import { View, Text } from '@lightningjs/solid'; | ||
import { Column, Row } from '@lightningjs/solid-primitives'; | ||
const RowStyles = { | ||
display: 'flex', | ||
justifyContent: 'flexStart', | ||
width: 1760, | ||
height: 300, | ||
gap: 26, | ||
y: 400, | ||
}; | ||
<Row gap={12} style={RowStyles}> | ||
<Button autofocus>TV Shows</Button> | ||
<Button>Movies</Button> | ||
<Button>Sports</Button> | ||
<Button>News</Button> | ||
</Row>; | ||
``` | ||
Additionally, flex will automatically layout Text nodes. Anytime a View with display: flex has children which are text nodes it adds a listener for the text to load to set the width and height of the text elements and then calls updateLayout on the container to recalculate the flex layout. | ||
```jsx | ||
<View gap={10} style={OverviewContainer}> | ||
<Text style={Title}>{data().title || data().name}</Text> | ||
<Text style={Overview}>{data().overview}</Text> | ||
<View gap={8} style={SupportContainer}> | ||
<Text style={Subline}>Support Text</Text> | ||
<Text style={Subline}>{data().release_date}</Text> | ||
<View width={30} height={30} src={'/assets/rt-popcorn.png'} /> | ||
<Text style={Subline}>90%</Text> | ||
</View> | ||
</View> | ||
``` | ||
`alignItems` supports `flexStart`, `flexEnd`, and `center` but requires it's container to have a height / width set. | ||
## Transitions / Animations | ||
You can define which properties will animate via the transition property along with setting custom `animationSettings`. If you wish to reuse or use the default `animationSettings`, you can set the property value to `true`. | ||
```jsx | ||
createEffect(on(activeElement, (elm) => { | ||
focusRingRef.x = elm.x; | ||
}, { defer: true})) | ||
<FocusRing animationSettings={{duration: 1500}} ref={focusRingRef} transition={{ x: true, scale: { duration: 1500, easing: 'ease-in-out'} }} /> | ||
``` | ||
For more complicated animations, you can access the Lightning renderer animate API directly: | ||
```jsx | ||
let button; | ||
onMount(() => { | ||
button.animate({ alpha: 1 }, { duration: 500 }); | ||
}); | ||
<Button ref={button}>Movies</Button>; | ||
``` | ||
To find out more about animations check out the [renderer example](https://github.com/lightning-js/renderer/blob/main/examples/tests/animation.ts#L70). | ||
### Global Animation Settings | ||
You can set default animation settings for all transitions globally via Config. | ||
```js | ||
import { Config } from '@lightningjs/solid'; | ||
Config.animationSettings = { | ||
duration: 250, | ||
delay: 0, | ||
repeat: 0, | ||
loop: false, | ||
stopMethod: false, | ||
easing: 'ease-in-out', | ||
}; | ||
``` | ||
Also you can disable all animations with `Config.animationsEnabled = false` | ||
## States | ||
The style object can also be used to style components based on their state. You can add any keys with states you'd like applied like so: | ||
```jsx | ||
const Button = { | ||
width: 386, | ||
height: 136, | ||
color: 0x546160ff, | ||
alpha: 0.5, | ||
scale: 1, | ||
focus: { | ||
color: 0x58807dff, | ||
scale: [1.1, { duration: 500 }], | ||
alpha: 1, | ||
}, | ||
disabled: { | ||
color: 0x333333ff, | ||
}, | ||
}; | ||
``` | ||
When Button is focused the focus styles will be applied. And when focus is removed, the original styles on the element will be set, meaning you need defaults on the original style to fallback to. | ||
To apply a state to a component: | ||
```jsx | ||
<Button states={{ active: true, happy: false, disabled: false }}>Movies</Button> | ||
<Button states={'active'}>Sports</Button> | ||
<Button states='happy'>News</Button> | ||
``` | ||
Or imperatively | ||
```jsx | ||
let myButton; | ||
createEffect(() => { | ||
myButton.states.add('focus'); | ||
// Check for a state | ||
if(myButton.states.has('focus')) { | ||
myButton.states.remove('focus') | ||
} | ||
myButton.states.add('disabled'); | ||
// is and has are identical | ||
myButton.states.is('disabled'); | ||
// toggle disabled on / off | ||
myButton.states.toggle('disabled'); | ||
}) | ||
<View ref={myButton} style={Button} /> | ||
``` | ||
The `focus` state is added and removed by the [useFocusManager](https://github.com/lightning-js/solid-primitives) primitive. Also note if elements are animating and another state is applied during the animation which uses the animated value (say alpha or color) - when that state is removed it will return to some value during the animation. Be careful not to set state with styles that are also being animated. | ||
#### stateMapperHook | ||
For further customization of styles using states, you can change the states before styles are applied globally using Config.stateMapperHook. For instance, if you wanted to change your styles based on another property like tone you could do: | ||
```js | ||
Config.stateMapperHook = (node, states) => { | ||
const tone = node.tone || ''; // node.tone is 'brand' | ||
return states.map((state) => state + tone); | ||
}; | ||
``` | ||
Then it would apply `focusbrand` from the styles object. | ||
### forwardStates | ||
When you want the state to also be applied to children elements, you can add `forwardStates` attribute to the parent element. Any states set on the parent will be add / removed from the children as well. This is useful for functional components where you need to change styles of children as well. | ||
```jsx | ||
function Button(props) { | ||
const ButtonContainer = { | ||
width: 386, | ||
height: 136, | ||
color: 0x000000ff, | ||
alpha: 0.3, | ||
scale: 1, | ||
transition: { | ||
scale: { duration: 1500, delay: 200, easing: 'easy-in' }, | ||
alpha: { duration: 1500, delay: 200, easing: 'easy-in' }, | ||
}, | ||
focus: { | ||
color: [0x58807dff, { duration: 2000 }], | ||
scale: 1.2, | ||
alpha: 1, | ||
}, | ||
}; | ||
const ButtonText = { | ||
fontSize: 32, | ||
lineHeight: Button.height, | ||
contain: 'width', | ||
textAlign: 'center', | ||
mountY: -0.35, | ||
color: 0xf6f6f9ff, | ||
height: Button.height, | ||
width: Button.width, | ||
focus: { | ||
color: 0xffffffff, | ||
}, | ||
}; | ||
return ( | ||
<View {...props} forwardStates style={ButtonContainer}> | ||
<Text style={ButtonText}>{props.children}</Text> | ||
</View> | ||
); | ||
} | ||
``` | ||
## Events | ||
`View` and `Text` provide a set of event handlers that can be used in various stages of a node creation process. | ||
```jsx | ||
onCreate: (target: ElementNode) | ||
onLoad: (target: INode, nodeLoadedPayload: NodeLoadedPayload) | ||
onFail: (target: INode, nodeFailedPayload: NodeFailedPayload) | ||
onBeforeLayout: (child: ElementNode, dimensions: Dimensions) | ||
onLayout: (child: ElementNode, dimensions: Dimensions) | ||
``` | ||
## Shaders and Effects | ||
The shader prop allows you to specify a custom shader. Most of the common use ones have shortcuts like `borderRadius`, `border`. | ||
```jsx | ||
const RoundedRectangle = ['RoundedRectangle', { radius: 6 }]; | ||
function Button(props) { | ||
return ( | ||
<View | ||
{...props} | ||
forwardStates | ||
style={buttonStyles.container} | ||
shader={RoundedRectangle} | ||
> | ||
<View style={buttonStyles.topBar} shader={RoundedRectangle}></View> | ||
<Text style={buttonStyles.text}>{props.children}</Text> | ||
</View> | ||
); | ||
} | ||
``` | ||
## use: (Directives) in Solid | ||
SolidJS has built in [Directives](https://www.solidjs.com/docs/latest/api#use___) support via `use:` property. These only work on root elements `node` and `text`. Meaning you can't use `View` or `Text` with directives so instead do: | ||
``` | ||
<node | ||
use:withPadding={[10, 15]} | ||
{...props} | ||
style={{ | ||
color: '#00000099', | ||
borderRadius: 8, | ||
border: { width: 2, color: '#ffffff' }, | ||
}} | ||
> | ||
``` | ||
PS - there is currently a [bug](https://github.com/solidjs/solid/issues/1927) in SolidJS that you need to have the directive before spreading props. | ||
## Config | ||
Allows setup of defaults for the app: | ||
```js | ||
import { Config } from '@lightningjs/solid'; | ||
// Log out solid rendering information | ||
// This is removed for Prod builds for performance | ||
Config.debug = false; | ||
//Animations (transitions) are enabled by default | ||
Config.animationsEnabled = true; | ||
// Set defaults for all <Text> | ||
Config.fontSettings.fontFamily = 'Ubuntu'; | ||
Config.fontSettings.color = 0xffffffff; | ||
Config.fontSettings.fontSize = 100; | ||
Config.stateMapperHook = (node, states) => { | ||
const tone = node.tone || ''; | ||
states.map((state) => state + tone); | ||
}; | ||
``` |
@@ -420,12 +420,18 @@ /* | ||
set style(value: SolidStyles) { | ||
set style(values: SolidStyles | SolidStyles[]) { | ||
const passedArray = isArray(values); | ||
const styleArray = passedArray ? values : [values]; | ||
// Keys set in JSX are more important | ||
for (const key in value) { | ||
// be careful of 0 values | ||
if (this[key as keyof SolidStyles] === undefined) { | ||
this[key as keyof SolidStyles] = value[key as keyof SolidStyles]; | ||
styleArray.forEach((value) => { | ||
for (const key in value) { | ||
// be careful of 0 values | ||
if (this[key as keyof SolidStyles] === undefined) { | ||
this[key as keyof SolidStyles] = value[key as keyof SolidStyles]; | ||
} | ||
} | ||
} | ||
this._style = value; | ||
}); | ||
// reverse the array so the first style is the most important | ||
this._style = ( | ||
passedArray ? Object.assign({}, ...styleArray.reverse()) : values | ||
) as SolidStyles; | ||
} | ||
@@ -432,0 +438,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
4628
285661
37