Skip to content

Commit 8d1f573

Browse files
committed
add borderbox v1
1 parent b0cbe2a commit 8d1f573

File tree

6 files changed

+123
-10
lines changed

6 files changed

+123
-10
lines changed

.prettierrc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"trailingComma": "all",
33
"singleQuote": true,
4-
"semi": true
4+
"semi": true,
5+
"printWidth": 120
56
}

docs/_sidebar.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
- [WebOs/LG](/deploy/lg.md)
3232
- Primitives
3333
- [Accessibility / Announcer](/primitives/a11y.md)
34+
- [borderBox](/primitives/borderBox.md)
3435
- [createBlurredImage](/primitives/createBlurredImage.md)
3536
- [createTag](/primitives/createTag.md)
3637
- [Dynamic](/primitives/dynamic.md)

docs/primitives/borderBox.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# `borderBox`
2+
3+
A SolidJS directive that adds a visual border component to an element when it receives focus.
4+
5+
### Usage
6+
7+
```tsx
8+
import { borderBox } from '@lightningtv/solid/primitives';
9+
10+
// Simple usage
11+
<view use:borderBox={true} />
12+
13+
// Usage with custom border space
14+
<view use:borderBox={{ borderSpace: 10 }} />
15+
16+
// Usage with custom border color (passed via NodeProps)
17+
<view use:borderBox={{ border: { color: 0xff0000, width: 4 } }} />
18+
```
19+
20+
### Parameters
21+
22+
The directive accepts `BorderProps | true | undefined` as the value.
23+
24+
### BorderProps
25+
26+
Extends standard [`NodeProps`](/solid/#/primitives/view).
27+
28+
| Prop | Type | Description | Default |
29+
| ------------- | -------- | ------------------------------------------------ | ------- |
30+
| `borderSpace` | `number` | The space between the element and the border box | `6` |
31+
32+
### Default Styles
33+
34+
The border box uses `BorderBoxStyle` defaults:
35+
36+
- `borderSpace`: 6
37+
- `borderRadius`: 20
38+
- `border`: `{ color: 0xffffff, width: 2 }`
39+
- `alpha`: 0 (initially, fades in to 1)
40+
41+
When the element gains focus, the border (a `view`) is created, inserted into the element, and `fadeIn` is called. When focus is lost, `fadeOut` is called and then the border is removed.

src/primitives/FadeInOut.tsx

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ElementNode, NodeProps, View } from '@lightningtv/solid';
1+
import { ElementNode, NodeProps, View } from '@lightningtv/solid';
22
import { Show } from 'solid-js';
33

44
interface Props {
@@ -14,6 +14,19 @@ const DEFAULT_PROPS = {
1414
easing: 'ease-in-out',
1515
};
1616

17+
export const ALPHA_NONE = { alpha: 0 };
18+
export const ALPHA_FULL = { alpha: 1 };
19+
20+
export function fadeIn(el: ElementNode): void {
21+
if (!el?.lng?.animate) return;
22+
el.alpha = 0;
23+
el.animate(ALPHA_FULL).start();
24+
}
25+
export function fadeOut(el: ElementNode): Promise<void> {
26+
if (!el?.lng?.animate) return Promise.resolve();
27+
return el.animate(ALPHA_NONE).start().waitUntilStopped();
28+
}
29+
1730
export default function FadeInOut(props: Props & NodeProps) {
1831
const config = Object.assign({}, DEFAULT_PROPS, props.transition);
1932
function onCreate(elm: ElementNode) {
@@ -23,12 +36,12 @@ export default function FadeInOut(props: Props & NodeProps) {
2336

2437
function onDestroy(elm: ElementNode) {
2538
elm.rtt = true;
26-
return elm.animate({ alpha: 0 }, { duration: config.duration, easing: config.easing })
27-
.start().waitUntilStopped();
39+
return elm.animate({ alpha: 0 }, { duration: config.duration, easing: config.easing }).start().waitUntilStopped();
2840
}
2941

3042
return (
3143
<Show when={props.when} keyed>
3244
<View {...props} onDestroy={onDestroy} onCreate={onCreate} />
33-
</Show>);
45+
</Show>
46+
);
3447
}

src/primitives/borderBox.tsx

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { ElementNode, insertNode, type NodeProps, type NodeStyles } from '@lightningtv/solid';
2+
import { createMemo, getOwner, onMount, runWithOwner, type Accessor } from 'solid-js';
3+
import { fadeIn, fadeOut } from './FadeInOut.jsx';
4+
import { chainFunctions } from '@lightningtv/solid/primitives';
5+
6+
export const BorderBoxStyle: NodeStyles = {
7+
alpha: 0,
8+
borderSpace: 6,
9+
borderRadius: 20,
10+
border: { color: 0xffffff, width: 2 },
11+
};
12+
13+
type BorderProps = NodeProps & { borderSpace?: number };
14+
const borderComponent = (props: BorderProps) => {
15+
const space = createMemo(() => props.borderSpace ?? (BorderBoxStyle.borderSpace as number));
16+
return (
17+
<>
18+
<view
19+
skipFocus
20+
onCreate={(el) => {
21+
const parent = el.parent!;
22+
el.width = parent.width + space() * 2;
23+
el.height = parent.height + space() * 2;
24+
fadeIn(el);
25+
}}
26+
onDestroy={fadeOut}
27+
style={BorderBoxStyle}
28+
x={-space()}
29+
y={-space()}
30+
{...props}
31+
/>
32+
</>
33+
);
34+
};
35+
36+
// Solid directives can only be used on native root elements `view` and `text`
37+
export default function borderbox(el: ElementNode, accessor: Accessor<BorderProps | true | undefined>) {
38+
let border: ElementNode | null;
39+
const owner = getOwner();
40+
41+
onMount(() => {
42+
el.onFocusChanged = chainFunctions((f) => {
43+
if (f) {
44+
if (border) return;
45+
runWithOwner(owner, () => {
46+
const props = accessor();
47+
border = borderComponent(
48+
props === true || props === undefined ? ({} as NodeProps) : props,
49+
) as any as ElementNode;
50+
insertNode(el, border);
51+
border.render();
52+
});
53+
} else if (border) {
54+
border.destroy();
55+
el.removeChild(border!);
56+
border = null;
57+
}
58+
}, el.onFocusChanged);
59+
});
60+
}

src/primitives/index.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export * from './useFocusManager.js';
22
export * from './announcer/index.js';
33
export * from './createInfiniteItems.js';
4+
export * from './borderBox.jsx';
45
export * from './useMouse.js';
56
export * from './portal.jsx';
67
export * from './Lazy.jsx';
@@ -23,11 +24,7 @@ export * from './VirtualGrid.jsx';
2324
export * from './Virtual.jsx';
2425
export * from './utils/withScrolling.js';
2526
export * from './createTag.jsx';
26-
export {
27-
type AnyFunction,
28-
chainFunctions,
29-
chainRefs,
30-
} from './utils/chainFunctions.js';
27+
export { type AnyFunction, chainFunctions, chainRefs } from './utils/chainFunctions.js';
3128
export * from './utils/handleNavigation.js';
3229
export { createSpriteMap, type SpriteDef } from './utils/createSpriteMap.js';
3330
export { createBlurredImage } from './utils/createBlurredImage.js';

0 commit comments

Comments
 (0)