Skip to content

Commit

Permalink
feat: serialize signals in object
Browse files Browse the repository at this point in the history
  • Loading branch information
ph1p committed Aug 25, 2024
1 parent 0f4f08e commit b45154f
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ import { h } from 'preact';
export default ({ signalsArray }) => {
return <div class="preact-signal-array">
<h1>{signalsArray[0]} {signalsArray[3]}</h1>
<p>{signalsArray[1].value}{signalsArray[2].value}{signalsArray[4].value}</p>
<p>{signalsArray[1].value}-{signalsArray[2].value}-{signalsArray[4].value}</p>
</div>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { h } from 'preact';

export default ({ signalsObject }) => {
return <div class="preact-signal-object">
<h1>{signalsObject.title}</h1>
<p>{signalsObject.counter.value}</p>
</div>
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { signal } from '@preact/signals';
import Signals from '../components/Signals';
import SignalsInArray from '../components/SignalsInArray';
import SignalsInObject from '../components/SignalsInObject';
const count = signal(1);
const secondCount = signal(2);
---
Expand All @@ -13,5 +14,6 @@ const secondCount = signal(2);
<Signals client:load count={count} />
<Signals client:load count={count} />
<SignalsInArray client:load signalsArray={["I'm not a signal", count, count, 12345, secondCount]} />
<SignalsInObject client:load signalsObject={{title:'I am a title', counter: count}} />
</body>
</html>
22 changes: 20 additions & 2 deletions packages/astro/test/preact-component.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,25 @@ describe('Preact component', () => {
],
});

assert.equal($('.preact-signal-array h1').text(), "I'm not a signal 12345");
assert.equal($('.preact-signal-array p').text(), '112');
assert.equal(element.find('h1').text(), "I'm not a signal 12345");
assert.equal(element.find('p').text(), '1-1-2');
});

it('Can use signals in object', async () => {
const html = await fixture.readFile('/signals/index.html');
const $ = cheerio.load(html);
const element = $('.preact-signal-object');
assert.equal(element.length, 1);

const sigs1Raw = $($('astro-island')[3]).attr('data-preact-signals');

const sigs1 = JSON.parse(sigs1Raw);

assert.deepEqual(sigs1, {
signalsObject: [['p0', 'counter']],
});

assert.equal(element.find('h1').text(), "I am a title");
assert.equal(element.find('p').text(), '1');
});
});
14 changes: 11 additions & 3 deletions packages/integrations/preact/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,21 @@ export default (element: HTMLElement) =>
);
for (const [propName, signalId] of Object.entries(signals)) {
if (Array.isArray(signalId)) {
signalId.forEach(([id, indexInPropsArray]) => {
const [valueOfSignal, indexInProps] = props[propName][indexInPropsArray];
signalId.forEach(([id, indexOrKeyInProps]) => {
const mapValue = props[propName][indexOrKeyInProps];
let valueOfSignal = mapValue;

// not an property key
if(typeof indexOrKeyInProps !== 'string') {
valueOfSignal = mapValue[0];
indexOrKeyInProps = mapValue[1];
}

if (!sharedSignalMap.has(id)) {
const signalValue = signal(valueOfSignal);
sharedSignalMap.set(id, signalValue);
}
props[propName][indexInProps] = sharedSignalMap.get(id);
props[propName][indexOrKeyInProps] = sharedSignalMap.get(id);
});
} else {
if (!sharedSignalMap.has(signalId)) {
Expand Down
24 changes: 15 additions & 9 deletions packages/integrations/preact/src/signals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,24 @@ export function serializeSignals(
map: PropNameToSignalMap,
) {
// Check for signals
const signals: Record<string, string | [string, number][]> = {};
const signals: Record<string, string | [string, number | string][]> = {};
for (const [key, value] of Object.entries(props)) {
if (Array.isArray(value)) {
value.forEach((signal, index) => {
// find signals in array. The index is important!
const isPropArray = Array.isArray(value);
const isPropObject = !isSignal(value) && typeof props[key] === 'object' && !isPropArray;

if (isPropObject || isPropArray) {
const values = isPropObject ? Object.keys(props[key]) : value;
values.forEach((valueKey: number | string, valueIndex: number) => {
const signal = isPropObject ? props[key][valueKey] : valueKey;
if (isSignal(signal)) {
props[key] = props[key].map((v: SignalLike, i: number) =>
i === index ? [signal.peek(), i] : v,
);
map.set(key, [...((map.get(key) || []) as []), [signal, index]]);
const keyOrIndex = isPropObject ? valueKey.toString() : valueIndex;

props[key] = isPropObject
? Object.assign({}, props[key], { [keyOrIndex]: signal.peek() })
: props[key].map((v: SignalLike, i: number) => (i === valueIndex ? signal.peek() : v));
map.set(key, [...((map.get(key) || []) as []), [signal, keyOrIndex]]);

signals[key] = [...((signals[key] || []) as []), [getSignalId(ctx, signal), index]];
signals[key] = [...((signals[key] || []) as []), [getSignalId(ctx, signal), keyOrIndex]];
}
});
} else if (isSignal(value)) {
Expand Down
2 changes: 1 addition & 1 deletion packages/integrations/preact/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export type SignalLike = {
peek(): any;
};

export type PropNameToSignalMap = Map<string, SignalLike | [SignalLike, number][]>;
export type PropNameToSignalMap = Map<string, SignalLike | [SignalLike, number | string][]>;

export type AstroPreactAttrs = {
['data-preact-signals']?: string;
Expand Down

0 comments on commit b45154f

Please sign in to comment.