@gtkx/react 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -35,8 +35,24 @@ type ListEntry = { item: unknown; handle: unknown };
35
35
  type ListState = { snapshot: unknown[]; entries: ListEntry[] };
36
36
  type DeferredState = { desired: unknown; isPresent: boolean; isScheduled: boolean; disconnect: (() => void) | null };
37
37
  type CanApply<P extends GObject.Object, V> = (object: P, value: V) => boolean;
38
+
39
+ type DeferredOps<P extends GObject.Object, V> = {
40
+ canApply?: CanApply<P, V> | undefined;
41
+ parse?: ((value: unknown) => V | undefined) | undefined;
42
+ read: (object: P) => unknown;
43
+ write: (object: P, value: V) => void;
44
+ signal?: string | undefined;
45
+ };
46
+
47
+ type DeferredHooks<P extends GObject.Object, V> = Omit<DeferredOps<P, V>, "read" | "write"> & {
48
+ read?: ((object: P) => unknown) | undefined;
49
+ write?: ((object: P, value: V) => void) | undefined;
50
+ };
51
+
38
52
  type ChildSetter = GObject.Object & { setChild: (child: Gtk.Widget | null) => void };
39
53
  type ContentSetter<C extends Gtk.Widget> = GObject.Object & { setContent: (content: C | null) => void };
54
+ type MatchingKey<P, F> = keyof P & string & { [K in keyof P]: P[K] extends F ? K : never }[keyof P];
55
+ type MethodKey<P, A> = MatchingKey<P, (argument: A) => unknown>;
40
56
 
41
57
  type BoxLike = GObject.Object & {
42
58
  remove: (child: Gtk.Widget) => void;
@@ -172,29 +188,31 @@ const list = <P extends GObject.Object, I, H = void>(
172
188
  };
173
189
 
174
190
  const settleDeferred = <P extends GObject.Object, V>(
175
- object: GObject.Object,
191
+ object: P,
176
192
  state: DeferredState,
177
193
  prop: string,
178
- canApply: CanApply<P, V> | undefined,
194
+ ops: DeferredOps<P, V>,
179
195
  ): void => {
180
- if (!state.isPresent || Object.is(Reflect.get(object, prop), state.desired)) {
196
+ if (!state.isPresent || Object.is(ops.read(object), state.desired)) {
181
197
  return;
182
198
  }
183
199
 
184
- if (canApply !== undefined && !canApply(object as P, state.desired as V)) {
200
+ const desired = state.desired as V;
201
+
202
+ if (ops.canApply !== undefined && !ops.canApply(object, desired)) {
185
203
  return;
186
204
  }
187
205
 
188
206
  applyWrite(prop, () => {
189
- Reflect.set(object, prop, state.desired);
207
+ ops.write(object, desired);
190
208
  });
191
209
  };
192
210
 
193
211
  const scheduleSettle = <P extends GObject.Object, V>(
194
- object: GObject.Object,
212
+ object: P,
195
213
  state: DeferredState,
196
214
  prop: string,
197
- canApply: CanApply<P, V> | undefined,
215
+ ops: DeferredOps<P, V>,
198
216
  ): void => {
199
217
  if (state.isScheduled) {
200
218
  return;
@@ -206,25 +224,25 @@ const scheduleSettle = <P extends GObject.Object, V>(
206
224
  state.isScheduled = false;
207
225
 
208
226
  if (state.disconnect !== null) {
209
- settleDeferred(object, state, prop, canApply);
227
+ settleDeferred(object, state, prop, ops);
210
228
  }
211
229
  });
212
230
  };
213
231
 
214
232
  const watchDrift = <P extends GObject.Object, V>(
215
- object: GObject.Object,
233
+ object: P,
216
234
  state: DeferredState,
217
235
  prop: string,
218
- canApply: CanApply<P, V> | undefined,
236
+ ops: DeferredOps<P, V>,
219
237
  ): void => {
220
238
  if (state.disconnect !== null) {
221
239
  return;
222
240
  }
223
241
 
224
- const signal = `notify::${getPropertyName(object, prop) ?? unsanitizeIdentifier(kebabCase(prop))}`;
242
+ const signal = ops.signal ?? `notify::${getPropertyName(object, prop) ?? unsanitizeIdentifier(kebabCase(prop))}`;
225
243
 
226
244
  const handler = (): undefined => {
227
- scheduleSettle(object, state, prop, canApply);
245
+ scheduleSettle(object, state, prop, ops);
228
246
  };
229
247
 
230
248
  object.on(signal, handler);
@@ -234,23 +252,20 @@ const watchDrift = <P extends GObject.Object, V>(
234
252
  };
235
253
  };
236
254
 
237
- const deferred = <P extends GObject.Object, V>(
238
- prop: string,
239
- canApply?: CanApply<P, V>,
240
- ): ElementBehavior<P> => ({
255
+ const deferredBehavior = <P extends GObject.Object, V>(prop: string, ops: DeferredOps<P, V>): ElementBehavior<P> => ({
241
256
  deferred: [prop],
242
257
  initialize: (): DeferredState => ({ desired: undefined, isPresent: false, isScheduled: false, disconnect: null }),
243
258
  update: (_object, _prev, next, context) => {
244
259
  const state = context as DeferredState;
245
- state.desired = next[prop];
246
- state.isPresent = next[prop] !== undefined;
260
+ state.desired = ops.parse === undefined ? next[prop] : ops.parse(next[prop]);
261
+ state.isPresent = state.desired !== undefined;
247
262
 
248
263
  return [prop];
249
264
  },
250
265
  flush: (object, context) => {
251
266
  const state = context as DeferredState;
252
- settleDeferred(object, state, prop, canApply);
253
- watchDrift(object, state, prop, canApply);
267
+ settleDeferred(object, state, prop, ops);
268
+ watchDrift(object, state, prop, ops);
254
269
  },
255
270
  teardown: (_object, context) => {
256
271
  const state = context as DeferredState;
@@ -259,6 +274,18 @@ const deferred = <P extends GObject.Object, V>(
259
274
  },
260
275
  });
261
276
 
277
+ const deferredWith = <P extends GObject.Object, V>(prop: string, hooks: DeferredHooks<P, V>): ElementBehavior<P> =>
278
+ deferredBehavior<P, V>(prop, {
279
+ ...hooks,
280
+ read: hooks.read ?? ((object) => Reflect.get(object, prop)),
281
+ write: hooks.write ?? ((object, value) => {
282
+ Reflect.set(object, prop, value);
283
+ }),
284
+ });
285
+
286
+ const deferred = <P extends GObject.Object, V>(prop: string, canApply?: CanApply<P, V>): ElementBehavior<P> =>
287
+ deferredWith<P, V>(prop, { canApply });
288
+
262
289
  const controlledText = (prop: string): ElementBehavior =>
263
290
  value(prop, (object, next) => {
264
291
  if (!hasSameText(object, prop, next)) {
@@ -301,12 +328,39 @@ const boxSlot = <P extends BoxLike>(): ElementBehavior<P> =>
301
328
  },
302
329
  });
303
330
 
304
- const addRemoveSlot = <C extends GObject.Object, P extends GObject.Object>(
331
+ const callMethod = <P extends GObject.Object, A>(parent: P, method: MethodKey<P, A>, argument: A): unknown =>
332
+ (parent[method] as (argument: A) => unknown)(argument);
333
+
334
+ const methodSlot = <P extends GObject.Object, C extends GObject.Object>(
335
+ slotName: string,
336
+ childType: string,
337
+ add: MethodKey<P, C>,
338
+ remove?: MethodKey<P, C>,
339
+ ): ElementBehavior => {
340
+ const hooks: SlotHooks<P, C> = { attach: (parent, child) => callMethod(parent, add, child) };
341
+
342
+ if (remove !== undefined) {
343
+ hooks.detach = (parent, child) => {
344
+ callMethod(parent, remove, child);
345
+ };
346
+ }
347
+
348
+ return slot<P, C>(slotName, childType, hooks);
349
+ };
350
+
351
+ const setterSlot = <P extends GObject.Object, C extends GObject.Object>(
305
352
  slotName: string,
306
353
  childType: string,
307
- add: (parent: P, child: C) => unknown,
308
- remove: (parent: P, child: C) => void,
309
- ): ElementBehavior => slot<P, C>(slotName, childType, { attach: add, detach: remove });
354
+ setter: MethodKey<P, C | null>,
355
+ ): ElementBehavior =>
356
+ slot<P, C>(slotName, childType, {
357
+ attach: (parent, child) => {
358
+ callMethod<P, C | null>(parent, setter, child);
359
+ },
360
+ detach: (parent) => {
361
+ callMethod<P, C | null>(parent, setter, null);
362
+ },
363
+ });
310
364
 
311
365
  const indexedSlot = <P extends IndexedChildHost<C>, C extends GObject.Object>(
312
366
  slotName: string,
@@ -325,12 +379,6 @@ const indexedSlot = <P extends IndexedChildHost<C>, C extends GObject.Object>(
325
379
  },
326
380
  });
327
381
 
328
- const adoptedChildrenSlot = <P extends GObject.Object, C extends GObject.Object>(
329
- childType: string,
330
- add: (parent: P, item: C) => unknown,
331
- remove: (parent: P, item: C) => void,
332
- ): ElementBehavior => addRemoveSlot<C, P>("children", childType, add, remove);
333
-
334
382
  const wrappedRow = <W extends Gtk.Widget>(
335
383
  Wrapper: new (props: Props) => W,
336
384
  setChild: (wrapper: W, inner: Gtk.Widget) => void,
@@ -394,12 +442,13 @@ export {
394
442
  value,
395
443
  list,
396
444
  deferred,
445
+ deferredWith,
397
446
  controlledText,
398
447
  childSetterSlot,
399
448
  contentSetterSlot,
400
449
  boxSlot,
401
- addRemoveSlot,
402
- adoptedChildrenSlot,
450
+ methodSlot,
451
+ setterSlot,
403
452
  indexedSlot,
404
453
  wrappingIndexedSlot,
405
454
  };