@kosdev-code/kos-dispense-sdk 0.1.0-next.971 → 0.1.0-next.975

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.
Files changed (50) hide show
  1. package/.kos.json +7 -0
  2. package/{dispense-registration-manager-Da9CW_cN.js → dispense-registration-manager-J59_vyyY.js} +584 -184
  3. package/dispense-registration-manager-J59_vyyY.js.map +1 -0
  4. package/{dispense-registration-manager-Dtb31fcI.cjs → dispense-registration-manager-OYGAiIZy.cjs} +551 -151
  5. package/dispense-registration-manager-OYGAiIZy.cjs.map +1 -0
  6. package/{extension-utils-D_dnBLH6.js → extension-utils-DEM1emOY.js} +2 -2
  7. package/{extension-utils-D_dnBLH6.js.map → extension-utils-DEM1emOY.js.map} +1 -1
  8. package/{extension-utils-Cfhe358W.cjs → extension-utils-Pd5ntKtv.cjs} +2 -2
  9. package/{extension-utils-Cfhe358W.cjs.map → extension-utils-Pd5ntKtv.cjs.map} +1 -1
  10. package/index.cjs +6 -4
  11. package/index.cjs.map +1 -1
  12. package/index.js +66 -64
  13. package/models/models/cui-kit-state/cui-kit-state-model.d.ts +131 -0
  14. package/models/models/cui-kit-state/cui-kit-state-model.d.ts.map +1 -0
  15. package/models/models/cui-kit-state/index.d.ts +4 -0
  16. package/models/models/cui-kit-state/index.d.ts.map +1 -0
  17. package/models/models/cui-kit-state/types/index.d.ts +68 -0
  18. package/models/models/index.d.ts +1 -0
  19. package/models/models/index.d.ts.map +1 -1
  20. package/models/utils/dispense-registration-manager.d.ts +13 -0
  21. package/models/utils/dispense-registration-manager.d.ts.map +1 -1
  22. package/models/utils/pour-context.d.ts +8 -0
  23. package/models/utils/pour-context.d.ts.map +1 -0
  24. package/models.cjs +4 -2
  25. package/models.cjs.map +1 -1
  26. package/models.js +17 -15
  27. package/package.json +5 -5
  28. package/{pump-provider-uNNMh9uh.cjs → pump-provider-BSbTT-BC.cjs} +371 -418
  29. package/pump-provider-BSbTT-BC.cjs.map +1 -0
  30. package/{pump-provider-BQmCKz7S.js → pump-provider-Ch7NOeRj.js} +417 -464
  31. package/pump-provider-Ch7NOeRj.js.map +1 -0
  32. package/ui/contexts/cui-kit/cui-kit.d.ts +12 -1
  33. package/ui/contexts/cui-kit/cui-kit.d.ts.map +1 -1
  34. package/ui/hooks/cui-kit/use-cui-kit.d.ts +44 -5
  35. package/ui/hooks/cui-kit/use-cui-kit.d.ts.map +1 -1
  36. package/ui/hooks/utils/pour-strategy/hold-to-pour-intent-strategy.d.ts.map +1 -1
  37. package/ui/hooks/utils/pour-strategy/hold-to-pour-strategy.d.ts.map +1 -1
  38. package/ui/hooks/utils/pour-strategy/pour-strategy.d.ts +2 -0
  39. package/ui/hooks/utils/pour-strategy/pour-strategy.d.ts.map +1 -1
  40. package/ui/hooks/utils/pour-strategy/tap-or-hold-to-pour-intent-strategy.d.ts.map +1 -1
  41. package/ui/utils/pour-context.d.ts +1 -7
  42. package/ui/utils/pour-context.d.ts.map +1 -1
  43. package/ui.cjs +3 -2
  44. package/ui.cjs.map +1 -1
  45. package/ui.js +49 -48
  46. package/ui.js.map +1 -1
  47. package/dispense-registration-manager-Da9CW_cN.js.map +0 -1
  48. package/dispense-registration-manager-Dtb31fcI.cjs.map +0 -1
  49. package/pump-provider-BQmCKz7S.js.map +0 -1
  50. package/pump-provider-uNNMh9uh.cjs.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import { jsx } from "@emotion/react/jsx-runtime";
2
- import { useKosModel, getKosCompanionModel, kosAutoEffect, KosLog, usePressGesture, EventBus, TimerManager } from "@kosdev-code/kos-ui-sdk";
3
- import { A as Assembly$1, a as Availability$1, B as Board$1, b as BoardContainer$1, N as Nozzle$1, D as DeviceAssembly, H as Holder$1, f as HolderContainer$1, I as Ingredient$1, g as IngredientContainer$1, G as GenericPour, h as Pump$1 } from "./dispense-registration-manager-Da9CW_cN.js";
2
+ import { useKosModel, getKosCompanionModel, kosAutoEffect, KosLog, whenReady, usePressGesture, TimerManager, EventBus } from "@kosdev-code/kos-ui-sdk";
4
3
  import React, { createContext, useMemo, useContext, useState, useCallback, useEffect, useRef } from "react";
4
+ import { A as Assembly$1, a as Availability$1, B as Board$1, b as BoardContainer$1, N as Nozzle$1, e as CuiKitState, X as cuiKitId, D as DeviceAssembly, H as Holder$1, g as HolderContainer$1, I as Ingredient$1, h as IngredientContainer$1, P as PourContext, G as GenericPour, j as Pump$1 } from "./dispense-registration-manager-J59_vyyY.js";
5
5
  const useAssembly = () => {
6
6
  const modelId = Assembly$1.type;
7
7
  const result = useKosModel({
@@ -192,30 +192,39 @@ const useBrands = ({
192
192
  clearBrand
193
193
  };
194
194
  };
195
- class PourContext {
196
- _data = /* @__PURE__ */ new Map();
197
- get(key) {
198
- return this._data.get(key);
199
- }
200
- set(key, value) {
201
- this._data.set(key, value);
202
- }
203
- remove(key) {
204
- this._data.delete(key);
205
- }
206
- has(key) {
207
- return this._data.has(key);
208
- }
209
- }
210
195
  const logger$7 = KosLog.createLogger({
196
+ name: "HoldToPourIntentStrategy",
197
+ group: "kos-hooks"
198
+ });
199
+ const HoldToPourIntentStrategy = {
200
+ name: "HoldToPourIntentStrategy",
201
+ requiresHold: true,
202
+ async initiate(model, intent) {
203
+ if (!model?.canPour) {
204
+ logger$7.debug(`${model?.id} cannot pour. Returning early.`);
205
+ return;
206
+ }
207
+ if (intent) {
208
+ logger$7.debug(`performing intent: ${intent}`);
209
+ await model?.performIntent(intent);
210
+ } else {
211
+ logger$7.debug("no intent provided. Returning early.");
212
+ }
213
+ },
214
+ async initiateEnded(nozzle) {
215
+ await nozzle?.cancelPour();
216
+ }
217
+ };
218
+ const logger$6 = KosLog.createLogger({
211
219
  name: "HoldToPourStrategy",
212
220
  group: "kos-hooks"
213
221
  });
214
222
  const HoldToPourStrategy = {
215
223
  name: "HoldToPourStrategy",
224
+ requiresHold: true,
216
225
  async initiate(nozzle) {
217
226
  if (!nozzle?.canPour) {
218
- logger$7.debug(`Nozzle ${nozzle?.id} cannot pour. Returning early.`);
227
+ logger$6.debug(`Nozzle ${nozzle?.id} cannot pour. Returning early.`);
219
228
  return;
220
229
  }
221
230
  await nozzle?.pour();
@@ -224,278 +233,172 @@ const HoldToPourStrategy = {
224
233
  await nozzle?.cancelPour();
225
234
  }
226
235
  };
227
- const logger$6 = KosLog.createLogger({
228
- name: "useNozzlePour",
236
+ const logger$5 = KosLog.createLogger({
237
+ name: "TapOrHoldIntentStrategy",
229
238
  group: "kos-hooks"
230
239
  });
231
- const useNozzlePour = ({
232
- onPour,
233
- onPourCancel,
234
- onBeforePour,
235
- onBeforePourRejected,
236
- pourContext,
237
- strategy = HoldToPourStrategy
238
- }) => {
239
- const { model: nozzle } = useNozzleContext();
240
- const [context, _setContext] = useState(
241
- pourContext || new PourContext()
242
- );
243
- const handlePour = useCallback(async () => {
244
- const canPour = onBeforePour ? await onBeforePour?.(context) : true;
245
- if (canPour) {
246
- logger$6.debug(
247
- `[${strategy?.name}] - Pour initiated for nozzle ${nozzle?.id}.`
248
- );
249
- strategy?.initiate(nozzle);
250
- onPour?.();
240
+ class TapOrHoldIntentStrategy {
241
+ intent;
242
+ name = "TapOrHoldIntentStrategy";
243
+ isHold = false;
244
+ disposer = null;
245
+ constructor(intent) {
246
+ this.intent = intent;
247
+ }
248
+ async initiate(model, intent = this.intent) {
249
+ this.isHold = false;
250
+ this.disposer = setTimeout(() => {
251
+ this.isHold = true;
252
+ }, 250);
253
+ if (model?.isPouring && !this.isHold) {
254
+ logger$5.debug(`additional tap detected. cancelling pour`);
255
+ await model.cancelPour();
256
+ return;
257
+ }
258
+ if (!model?.canPour) {
259
+ logger$5.debug(`${model?.id} cannot pour. Returning early.`);
260
+ return;
261
+ }
262
+ if (intent) {
263
+ logger$5.debug(`performing intent: ${intent}`);
264
+ await model?.performIntent(intent);
251
265
  } else {
252
- logger$6.debug(
253
- `[${strategy?.name}] - Did not pass onBeforePour gate for nozzle ${nozzle?.id}. Cancelling pour.`
254
- );
255
- onBeforePourRejected?.();
266
+ logger$5.debug("no intent provided. Returning early.");
256
267
  }
257
- }, [
258
- onPour,
259
- nozzle,
260
- strategy,
261
- onBeforePour,
262
- onPourCancel,
263
- context,
264
- onBeforePourRejected
265
- ]);
266
- const handleCancelPour = useCallback(() => {
267
- logger$6.debug(
268
- `[${strategy?.name}] - Pour initiate ended called for nozzle ${nozzle?.id}.`
269
- );
270
- strategy?.initiateEnded?.(nozzle);
271
- onPourCancel?.();
272
- }, [onPourCancel, nozzle, strategy]);
273
- const forceCancelPour = useCallback(() => {
274
- logger$6.debug(`Force cancelling pour for nozzle ${nozzle?.id}.`);
275
- nozzle?.cancelPour();
276
- onPourCancel?.();
277
- }, [onPourCancel, nozzle]);
278
- const handleUpdateContext = useCallback(
279
- (key, value) => {
280
- context.set(key, value);
281
- },
282
- [context]
283
- );
284
- const handleRemoveFromContext = useCallback(
285
- (key) => {
286
- context.remove(key);
287
- },
288
- [context]
289
- );
290
- const handlers = usePressGesture({
291
- onPressed: handlePour,
292
- onRelease: handleCancelPour
293
- });
294
- return {
295
- handlers: handlers.dom,
296
- cancel: forceCancelPour,
297
- updatePourContext: handleUpdateContext,
298
- removeFromPourContext: handleRemoveFromContext,
299
- status: nozzle
300
- };
268
+ }
269
+ async initiateEnded(nozzle) {
270
+ if (this.disposer) clearTimeout(this.disposer);
271
+ this.disposer = null;
272
+ if (!this.isHold) {
273
+ logger$5.debug("Tap detected. don't cancel and return.");
274
+ return true;
275
+ }
276
+ await nozzle?.cancelPour();
277
+ return false;
278
+ }
279
+ }
280
+ const logger$4 = KosLog.createLogger({
281
+ name: "TapToPourIntentStrategy",
282
+ group: "kos-hooks"
283
+ });
284
+ const TapToPourIntentStrategy = {
285
+ name: "TapToPourIntentStrategy",
286
+ async initiate(model, intent) {
287
+ if (model?.isPouring) {
288
+ logger$4.debug(`Nozzle ${model?.id} is already pouring. cancelling pour`);
289
+ await model.cancelPour();
290
+ return;
291
+ }
292
+ if (!model?.canPour) {
293
+ logger$4.debug(`Nozzle ${model?.id} cannot pour. Returning early.`);
294
+ return;
295
+ }
296
+ if (intent) {
297
+ logger$4.debug(`performing intent: ${intent}`);
298
+ await model?.performIntent(intent);
299
+ } else {
300
+ logger$4.debug("no intent provided. Returning early.");
301
+ }
302
+ },
303
+ async initiateEnded(_nozzle) {
304
+ return true;
305
+ }
301
306
  };
302
- const useFlavors = ({
303
- onFlavorSelected,
304
- onFlavorCleared,
305
- companionType
306
- }) => {
307
- const { model: nozzle } = useNozzleContext();
308
- const flavors = useMemo(
309
- () => nozzle?.getGroupAvailabilityItems("flavors").filter((f) => f.visible) ?? [],
310
- [nozzle]
311
- );
312
- const companions = useMemo(() => {
313
- if (!companionType) return [];
314
- return flavors.map(
315
- (flavor) => getKosCompanionModel(flavor, companionType)
316
- );
317
- }, [flavors, companionType]);
318
- const [selectedFlavors, setSelectedFlavors] = useState([]);
319
- const selectFlavor = useCallback(
320
- async (flavor, context, forceUpdate) => {
321
- setSelectedFlavors(
322
- (previous) => previous?.includes(flavor) ? [...previous.filter((p) => p !== flavor)] : [...previous ?? [], flavor]
323
- );
324
- if (nozzle?.selectedPourable) {
325
- const { beverage } = nozzle.selectedPourable;
326
- await nozzle.setSelectedPourable(
327
- {
328
- beverage,
329
- additional: selectedFlavors ?? [],
330
- context
331
- },
332
- forceUpdate
333
- );
334
- }
335
- onFlavorSelected?.(flavor, selectedFlavors ?? []);
336
- },
337
- [nozzle, onFlavorSelected, selectedFlavors]
338
- );
339
- const clear = useCallback(async () => {
340
- setSelectedFlavors([]);
341
- if (nozzle?.selectedPourable) {
342
- const { beverage } = nozzle.selectedPourable;
343
- nozzle.setSelectedPourable({ beverage });
344
- onFlavorCleared?.();
307
+ const logger$3 = KosLog.createLogger({
308
+ name: "HoldToPourStrategy",
309
+ group: "kos-hooks"
310
+ });
311
+ const TapToPourStrategy = {
312
+ name: "TapToPourStrategy",
313
+ async initiate(nozzle) {
314
+ if (nozzle?.isPouring) {
315
+ logger$3.debug(`Nozzle ${nozzle?.id} is already pouring. cancelling pour`);
316
+ await nozzle.cancelPour();
317
+ return;
345
318
  }
346
- }, [nozzle, onFlavorCleared]);
347
- return {
348
- flavors,
349
- flavorCompanions: companions,
350
- selectFlavor,
351
- selectedFlavors,
352
- clearFlavors: clear
353
- };
319
+ if (!nozzle?.canPour) {
320
+ logger$3.debug(`Nozzle ${nozzle?.id} cannot pour. Returning early.`);
321
+ return;
322
+ }
323
+ await nozzle?.pour();
324
+ },
325
+ async initiateEnded(_nozzle) {
326
+ return true;
327
+ }
354
328
  };
355
329
  const DISPENSE_GROUP_BRAND = "brands";
356
330
  const DISPENSE_GROUP_BEVERAGE = "beverages";
357
331
  const DISPENSE_GROUP_FLAVOR = "flavors";
358
332
  const log$2 = KosLog.createLogger({ name: "use-cui-kit" });
359
333
  const useCuiKit = ({
360
- initialGroup = DISPENSE_GROUP_BRAND,
334
+ initialGroup,
335
+ steps,
361
336
  options = {}
362
337
  }) => {
363
- const [currentGroup, setCurrentGroup] = useState(initialGroup);
364
338
  const { model: nozzle } = useNozzleContext();
365
339
  const timersRef = useRef([]);
366
- const updatePourContext = useRef();
367
- const removeFromPourContext = useRef();
368
- const onBrandSelected = useCallback(
369
- (model) => {
370
- setCurrentGroup(DISPENSE_GROUP_BEVERAGE);
371
- const topic = `/kos/dispense/${nozzle?.path}/brand/selected/`;
372
- EventBus.publish(topic, model);
373
- },
374
- [nozzle?.path]
375
- );
376
- const onBrandCleared = useCallback(() => {
377
- const topic = `/kos/dispense/${nozzle?.path}/brand/cleared/`;
378
- EventBus.publish(topic, {});
379
- }, [nozzle?.path]);
380
- const onBeverageListUpdated = useCallback(
381
- (beverages2) => {
382
- const topic = `/kos/dispense/${nozzle?.path}/beverage/list/updated/`;
383
- EventBus.publish(topic, beverages2);
384
- },
385
- [nozzle?.path]
386
- );
387
- const onBeverageSelected = useCallback(
388
- (model) => {
389
- setCurrentGroup(DISPENSE_GROUP_FLAVOR);
390
- updatePourContext.current?.("selectedBeverage", model);
391
- const topic = `/kos/dispense/${nozzle?.path}/beverage/selected/`;
392
- EventBus.publish(topic, model);
393
- },
394
- [nozzle?.path]
395
- );
396
- const onBeverageCleared = useCallback(() => {
397
- const topic = `/kos/dispense/${nozzle?.path}/beverage/cleared/`;
398
- removeFromPourContext.current?.("selectedBeverage");
399
- EventBus.publish(topic, {});
400
- }, [nozzle?.path]);
401
- const onFlavorSelected = useCallback(
402
- (model) => {
403
- const topic = `/kos/dispense/${nozzle?.path}/flavor/selected/`;
404
- EventBus.publish(topic, model);
405
- },
406
- [nozzle?.path]
407
- );
408
- const onFlavorCleared = useCallback(() => {
409
- const topic = `/kos/dispense/${nozzle?.path}/flavor/cleared/`;
410
- EventBus.publish(topic, {});
411
- }, [nozzle?.path]);
412
- const onPour = useCallback(() => {
413
- const topic = `/kos/dispense/${nozzle?.path}/pour/`;
414
- EventBus.publish(topic, {});
415
- }, [nozzle?.path]);
416
- const handleBeforePour = useCallback(
417
- async (context) => {
418
- const topic = `/kos/dispense/${nozzle?.path}/pre/pour/`;
419
- EventBus.publish(topic, {});
420
- return await onBeforePour?.(context) || true;
340
+ const kit = useMemo(() => {
341
+ if (!nozzle) return void 0;
342
+ try {
343
+ return CuiKitState.instance(cuiKitId(nozzle.path)).options({
344
+ nozzlePath: nozzle.path,
345
+ initialGroup: steps ? initialGroup : initialGroup ?? DISPENSE_GROUP_BRAND,
346
+ steps,
347
+ companionTypes: options.companionTypes,
348
+ pour: { strategy: options.pour?.strategy ?? HoldToPourStrategy },
349
+ onPourComplete: options.onPourComplete
350
+ }).build();
351
+ } catch (error) {
352
+ throw new Error(
353
+ `The CUI Kit models are not registered: call KosModelRegistry.cuiKit.models() in the app's registration chain (${error.message})`
354
+ );
355
+ }
356
+ }, [nozzle]);
357
+ const [ready, setReady] = useState(false);
358
+ const [, setVersion] = useState(0);
359
+ useEffect(() => {
360
+ if (!kit) return void 0;
361
+ let dispose;
362
+ let cancelled = false;
363
+ let attached = false;
364
+ whenReady(kit).then(() => {
365
+ if (cancelled) return;
366
+ attached = true;
367
+ if (kit.attach()) {
368
+ kit.reset();
369
+ }
370
+ setReady(true);
371
+ dispose = kosAutoEffect(() => {
372
+ readKitState(kit);
373
+ setVersion((version) => version + 1);
374
+ });
375
+ });
376
+ return () => {
377
+ cancelled = true;
378
+ dispose?.();
379
+ if (attached) kit.detach();
380
+ };
381
+ }, [kit]);
382
+ const updateOnBeforePour = useCallback(
383
+ (callback) => {
384
+ kit?.setBeforePour(callback);
421
385
  },
422
- [nozzle?.path]
386
+ [kit]
423
387
  );
424
- const handleBeforePourRejected = useCallback(async () => {
425
- const topic = `/kos/dispense/${nozzle?.path}/pre/pour/rejected/`;
426
- EventBus.publish(topic, {});
427
- }, [nozzle?.path]);
428
- const onPourCancel = useCallback(() => {
429
- const topic = `/kos/dispense/${nozzle?.path}/pour/cancel/`;
430
- EventBus.publish(topic, {});
431
- }, [nozzle?.path]);
432
- const { brands, selectBrand, selectedBrand, clearBrand, brandCompanions } = useBrands({
433
- companionType: options.companionTypes?.brand,
434
- onBrandSelected,
435
- onBrandCleared
436
- });
437
- const {
438
- beverages,
439
- selectBeverage,
440
- selectedBeverage,
441
- clearBeverage,
442
- beverageCompanions
443
- } = useBeverages({
444
- companionType: options.companionTypes?.beverage,
445
- onBeverageSelected,
446
- onClearBeverage: onBeverageCleared,
447
- onBeverageListUpdated
448
- });
449
- const {
450
- clearFlavors,
451
- flavors,
452
- selectFlavor,
453
- selectedFlavors,
454
- flavorCompanions
455
- } = useFlavors({
456
- companionType: options.companionTypes?.flavor,
457
- onFlavorSelected,
458
- onFlavorCleared
459
- });
460
- const [onBeforePour, setOnBeforePour] = useState();
461
- const updateOnBeforePour = useCallback((callback) => {
462
- setOnBeforePour(() => callback);
463
- }, []);
464
- const pour = useNozzlePour({
465
- onPour,
466
- onPourCancel,
467
- onBeforePour: handleBeforePour,
468
- onBeforePourRejected: handleBeforePourRejected
388
+ const gesture = usePressGesture({
389
+ onPressed: () => void kit?.pressPour(),
390
+ onRelease: () => kit?.releasePour()
469
391
  });
470
- updatePourContext.current = pour.updatePourContext;
471
- removeFromPourContext.current = pour.removeFromPourContext;
472
- const back = useCallback(() => {
473
- log$2.debug(`Navigating back from group: ${currentGroup}`);
474
- switch (currentGroup) {
475
- case DISPENSE_GROUP_BRAND:
476
- clearBeverage();
477
- clearBrand();
478
- break;
479
- case DISPENSE_GROUP_BEVERAGE:
480
- case DISPENSE_GROUP_FLAVOR:
481
- setCurrentGroup(initialGroup);
482
- clearBeverage();
483
- clearBrand();
484
- break;
485
- }
486
- }, [clearBeverage, clearBrand, currentGroup, initialGroup]);
487
- const home = useCallback(() => {
488
- const topic = `/kos/dispense/${nozzle?.path}/home/`;
489
- EventBus.publish(topic, {});
490
- setCurrentGroup(initialGroup);
491
- if (initialGroup === DISPENSE_GROUP_BRAND) {
492
- clearBeverage();
493
- clearBrand();
494
- }
495
- if (initialGroup === DISPENSE_GROUP_BEVERAGE) {
496
- clearBeverage();
497
- }
498
- }, [clearBeverage, clearBrand, initialGroup]);
392
+ const pour = {
393
+ handlers: gesture.dom,
394
+ cancel: () => void kit?.cancelPour(),
395
+ start: async (pourOptions) => await kit?.startPour(pourOptions) ?? false,
396
+ press: async (pourOptions) => await kit?.pressPour(pourOptions) ?? false,
397
+ release: (pourOptions) => kit?.releasePour(pourOptions),
398
+ updatePourContext: (key, value) => kit?.pourContext.set(key, value),
399
+ removeFromPourContext: (key) => kit?.pourContext.remove(key),
400
+ status: nozzle
401
+ };
499
402
  const pauseTimers = useCallback(() => {
500
403
  if (timersRef.current.length === 0) {
501
404
  log$2.debug("No timers to pause.");
@@ -528,6 +431,7 @@ const useCuiKit = ({
528
431
  }
529
432
  timersRef.current.push(timer);
530
433
  }, []);
434
+ const active = ready ? kit : void 0;
531
435
  return {
532
436
  timers: {
533
437
  add: addTimer,
@@ -535,43 +439,66 @@ const useCuiKit = ({
535
439
  restart: restartTimers
536
440
  },
537
441
  nav: {
538
- currentGroup,
539
- back,
540
- home
442
+ currentGroup: active?.currentGroup ?? (steps ? steps.brands ? DISPENSE_GROUP_BRAND : DISPENSE_GROUP_BEVERAGE : initialGroup ?? DISPENSE_GROUP_BRAND),
443
+ back: () => active?.back(),
444
+ home: () => active?.home(),
445
+ steps: active?.enabledSteps,
446
+ canBack: active?.canBack ?? false,
447
+ toPour: (pourOptions) => active?.toPour(pourOptions)
541
448
  },
542
449
  pour,
543
450
  updateOnBeforePour,
544
451
  selections: {
545
- [DISPENSE_GROUP_BRAND]: selectedBrand,
546
- [DISPENSE_GROUP_BEVERAGE]: selectedBeverage,
547
- [DISPENSE_GROUP_FLAVOR]: selectedFlavors
452
+ [DISPENSE_GROUP_BRAND]: active?.selectedBrand ?? null,
453
+ [DISPENSE_GROUP_BEVERAGE]: active?.selectedBeverage ?? null,
454
+ [DISPENSE_GROUP_FLAVOR]: active?.selectedFlavors ?? []
548
455
  },
549
456
  [DISPENSE_GROUP_BRAND]: {
550
- models: brands,
551
- companions: brandCompanions,
552
- clear: clearBrand,
457
+ models: active?.brands ?? [],
458
+ companions: active?.brandCompanions ?? [],
459
+ clear: () => active?.clearBrand(),
553
460
  handlers: {
554
- onSelected: selectBrand
461
+ onSelected: async (model, context, forceUpdate) => {
462
+ if (forceUpdate) {
463
+ KosLog.warn(
464
+ `forceUpdate is not currently supported when setting brand`
465
+ );
466
+ }
467
+ active?.selectBrand(model, context);
468
+ }
555
469
  }
556
470
  },
557
471
  [DISPENSE_GROUP_BEVERAGE]: {
558
- models: beverages,
559
- companions: beverageCompanions,
560
- clear: clearBeverage,
472
+ models: active?.beverages ?? [],
473
+ companions: active?.beverageCompanions ?? [],
474
+ clear: () => void active?.clearBeverage(),
561
475
  handlers: {
562
- onSelected: selectBeverage
476
+ onSelected: async (model, context, forceUpdate) => active?.selectBeverage(model, context, forceUpdate)
563
477
  }
564
478
  },
565
479
  [DISPENSE_GROUP_FLAVOR]: {
566
- models: flavors,
567
- companions: flavorCompanions,
568
- clear: clearFlavors,
480
+ models: active?.flavors ?? [],
481
+ companions: active?.flavorCompanions ?? [],
482
+ clear: () => void active?.clearFlavors(),
569
483
  handlers: {
570
- onSelected: selectFlavor
484
+ onSelected: async (model, context, forceUpdate) => active?.selectFlavor(model, context, forceUpdate)
571
485
  }
572
486
  }
573
487
  };
574
488
  };
489
+ const readKitState = (kit) => [
490
+ kit.currentGroup,
491
+ kit.canBack,
492
+ kit.selectedBrand,
493
+ kit.selectedBeverage,
494
+ ...kit.selectedFlavors,
495
+ ...kit.brands,
496
+ ...kit.beverages,
497
+ ...kit.flavors,
498
+ ...kit.brandCompanions,
499
+ ...kit.beverageCompanions,
500
+ ...kit.flavorCompanions
501
+ ];
575
502
  const useAmbientAttractTimers = (props) => {
576
503
  const { model: nozzle } = useNozzleContext();
577
504
  const [attractTimer, setAttractTimer] = useState(null);
@@ -1121,6 +1048,59 @@ const useDefaultCuiKitLifecycle = ({
1121
1048
  restartScreenTimer: ambientRestart
1122
1049
  };
1123
1050
  };
1051
+ const useFlavors = ({
1052
+ onFlavorSelected,
1053
+ onFlavorCleared,
1054
+ companionType
1055
+ }) => {
1056
+ const { model: nozzle } = useNozzleContext();
1057
+ const flavors = useMemo(
1058
+ () => nozzle?.getGroupAvailabilityItems("flavors").filter((f) => f.visible) ?? [],
1059
+ [nozzle]
1060
+ );
1061
+ const companions = useMemo(() => {
1062
+ if (!companionType) return [];
1063
+ return flavors.map(
1064
+ (flavor) => getKosCompanionModel(flavor, companionType)
1065
+ );
1066
+ }, [flavors, companionType]);
1067
+ const [selectedFlavors, setSelectedFlavors] = useState([]);
1068
+ const selectFlavor = useCallback(
1069
+ async (flavor, context, forceUpdate) => {
1070
+ setSelectedFlavors(
1071
+ (previous) => previous?.includes(flavor) ? [...previous.filter((p) => p !== flavor)] : [...previous ?? [], flavor]
1072
+ );
1073
+ if (nozzle?.selectedPourable) {
1074
+ const { beverage } = nozzle.selectedPourable;
1075
+ await nozzle.setSelectedPourable(
1076
+ {
1077
+ beverage,
1078
+ additional: selectedFlavors ?? [],
1079
+ context
1080
+ },
1081
+ forceUpdate
1082
+ );
1083
+ }
1084
+ onFlavorSelected?.(flavor, selectedFlavors ?? []);
1085
+ },
1086
+ [nozzle, onFlavorSelected, selectedFlavors]
1087
+ );
1088
+ const clear = useCallback(async () => {
1089
+ setSelectedFlavors([]);
1090
+ if (nozzle?.selectedPourable) {
1091
+ const { beverage } = nozzle.selectedPourable;
1092
+ nozzle.setSelectedPourable({ beverage });
1093
+ onFlavorCleared?.();
1094
+ }
1095
+ }, [nozzle, onFlavorCleared]);
1096
+ return {
1097
+ flavors,
1098
+ flavorCompanions: companions,
1099
+ selectFlavor,
1100
+ selectedFlavors,
1101
+ clearFlavors: clear
1102
+ };
1103
+ };
1124
1104
  const useDeviceAssembly = () => {
1125
1105
  const modelId = DeviceAssembly.type;
1126
1106
  const result = useKosModel({
@@ -1175,29 +1155,7 @@ const useIngredient = () => {
1175
1155
  });
1176
1156
  return result;
1177
1157
  };
1178
- const logger$5 = KosLog.createLogger({
1179
- name: "HoldToPourIntentStrategy",
1180
- group: "kos-hooks"
1181
- });
1182
- const HoldToPourIntentStrategy = {
1183
- name: "HoldToPourIntentStrategy",
1184
- async initiate(model, intent) {
1185
- if (!model?.canPour) {
1186
- logger$5.debug(`${model?.id} cannot pour. Returning early.`);
1187
- return;
1188
- }
1189
- if (intent) {
1190
- logger$5.debug(`performing intent: ${intent}`);
1191
- await model?.performIntent(intent);
1192
- } else {
1193
- logger$5.debug("no intent provided. Returning early.");
1194
- }
1195
- },
1196
- async initiateEnded(nozzle) {
1197
- await nozzle?.cancelPour();
1198
- }
1199
- };
1200
- const logger$4 = KosLog.createLogger({
1158
+ const logger$2 = KosLog.createLogger({
1201
1159
  name: "useNozzlePour",
1202
1160
  group: "kos-hooks"
1203
1161
  });
@@ -1208,21 +1166,21 @@ const useIngredientPour = ({
1208
1166
  strategy = HoldToPourIntentStrategy
1209
1167
  }) => {
1210
1168
  const handlePour = useCallback(() => {
1211
- logger$4.debug(
1169
+ logger$2.debug(
1212
1170
  `[${strategy?.name}] - Pour initiated for model ${intentContainer?.id}.`
1213
1171
  );
1214
1172
  strategy?.initiate(intentContainer);
1215
1173
  onPour?.();
1216
1174
  }, [onPour, intentContainer, strategy]);
1217
1175
  const handleCancelPour = useCallback(() => {
1218
- logger$4.debug(
1176
+ logger$2.debug(
1219
1177
  `[${strategy?.name}] - Pour initiate ended called for nozzle ${intentContainer?.id}.`
1220
1178
  );
1221
1179
  strategy?.initiateEnded?.(intentContainer);
1222
1180
  onPourCancel?.();
1223
1181
  }, [onPourCancel, intentContainer, strategy]);
1224
1182
  const forceCancelPour = useCallback(() => {
1225
- logger$4.debug(`Force cancelling pour for nozzle ${intentContainer?.id}.`);
1183
+ logger$2.debug(`Force cancelling pour for nozzle ${intentContainer?.id}.`);
1226
1184
  intentContainer?.cancelPour();
1227
1185
  onPourCancel?.();
1228
1186
  }, [onPourCancel, intentContainer]);
@@ -1263,7 +1221,82 @@ function withNozzle(path, WrappedComponent) {
1263
1221
  return /* @__PURE__ */ jsx(KosModelLoader, { ...status, children: /* @__PURE__ */ jsx(WrappedComponent, { ...props, nozzle: model }) });
1264
1222
  };
1265
1223
  }
1266
- const logger$3 = KosLog.createLogger({
1224
+ const logger$1 = KosLog.createLogger({
1225
+ name: "useNozzlePour",
1226
+ group: "kos-hooks"
1227
+ });
1228
+ const useNozzlePour = ({
1229
+ onPour,
1230
+ onPourCancel,
1231
+ onBeforePour,
1232
+ onBeforePourRejected,
1233
+ pourContext,
1234
+ strategy = HoldToPourStrategy
1235
+ }) => {
1236
+ const { model: nozzle } = useNozzleContext();
1237
+ const [context, _setContext] = useState(
1238
+ pourContext || new PourContext()
1239
+ );
1240
+ const handlePour = useCallback(async () => {
1241
+ const canPour = onBeforePour ? await onBeforePour?.(context) : true;
1242
+ if (canPour) {
1243
+ logger$1.debug(
1244
+ `[${strategy?.name}] - Pour initiated for nozzle ${nozzle?.id}.`
1245
+ );
1246
+ strategy?.initiate(nozzle);
1247
+ onPour?.();
1248
+ } else {
1249
+ logger$1.debug(
1250
+ `[${strategy?.name}] - Did not pass onBeforePour gate for nozzle ${nozzle?.id}. Cancelling pour.`
1251
+ );
1252
+ onBeforePourRejected?.();
1253
+ }
1254
+ }, [
1255
+ onPour,
1256
+ nozzle,
1257
+ strategy,
1258
+ onBeforePour,
1259
+ onPourCancel,
1260
+ context,
1261
+ onBeforePourRejected
1262
+ ]);
1263
+ const handleCancelPour = useCallback(() => {
1264
+ logger$1.debug(
1265
+ `[${strategy?.name}] - Pour initiate ended called for nozzle ${nozzle?.id}.`
1266
+ );
1267
+ strategy?.initiateEnded?.(nozzle);
1268
+ onPourCancel?.();
1269
+ }, [onPourCancel, nozzle, strategy]);
1270
+ const forceCancelPour = useCallback(() => {
1271
+ logger$1.debug(`Force cancelling pour for nozzle ${nozzle?.id}.`);
1272
+ nozzle?.cancelPour();
1273
+ onPourCancel?.();
1274
+ }, [onPourCancel, nozzle]);
1275
+ const handleUpdateContext = useCallback(
1276
+ (key, value) => {
1277
+ context.set(key, value);
1278
+ },
1279
+ [context]
1280
+ );
1281
+ const handleRemoveFromContext = useCallback(
1282
+ (key) => {
1283
+ context.remove(key);
1284
+ },
1285
+ [context]
1286
+ );
1287
+ const handlers = usePressGesture({
1288
+ onPressed: handlePour,
1289
+ onRelease: handleCancelPour
1290
+ });
1291
+ return {
1292
+ handlers: handlers.dom,
1293
+ cancel: forceCancelPour,
1294
+ updatePourContext: handleUpdateContext,
1295
+ removeFromPourContext: handleRemoveFromContext,
1296
+ status: nozzle
1297
+ };
1298
+ };
1299
+ const logger = KosLog.createLogger({
1267
1300
  name: "usePour",
1268
1301
  group: "kos-hooks"
1269
1302
  });
@@ -1280,12 +1313,12 @@ const usePourTarget = ({
1280
1313
  pourContext || new PourContext()
1281
1314
  );
1282
1315
  const handlePour = useCallback(async () => {
1283
- logger$3.debug(
1316
+ logger.debug(
1284
1317
  `[${strategy?.name}] - calling before pour for pour target ${pourTarget?.id}.`
1285
1318
  );
1286
1319
  const canPour = onBeforePour ? await onBeforePour?.(context) : true;
1287
1320
  if (canPour) {
1288
- logger$3.debug(
1321
+ logger.debug(
1289
1322
  `[${strategy?.name}] - Pour initiated for pour target ${pourTarget?.id}.`
1290
1323
  );
1291
1324
  await strategy?.initiate(pourTarget);
@@ -1294,14 +1327,14 @@ const usePourTarget = ({
1294
1327
  onPourAbort?.();
1295
1328
  }
1296
1329
  } else {
1297
- logger$3.debug(
1330
+ logger.debug(
1298
1331
  `[${strategy?.name}] - Pour cancelled for pour target ${pourTarget?.id}.`
1299
1332
  );
1300
1333
  onPourCancel?.();
1301
1334
  }
1302
1335
  }, [onPour, onBeforePour, onPourAbort, pourTarget, strategy, context]);
1303
1336
  const handleCancelPour = useCallback(async () => {
1304
- logger$3.debug(
1337
+ logger.debug(
1305
1338
  `[${strategy?.name}] - Pour initiate ended called for pour target ${pourTarget?.id}.`
1306
1339
  );
1307
1340
  const ignore = await strategy?.initiateEnded?.(pourTarget);
@@ -1310,7 +1343,7 @@ const usePourTarget = ({
1310
1343
  }
1311
1344
  }, [onPourCancel, pourTarget, strategy]);
1312
1345
  const forceCancelPour = useCallback(async () => {
1313
- logger$3.debug(`Force cancelling pour for pour target ${pourTarget?.id}.`);
1346
+ logger.debug(`Force cancelling pour for pour target ${pourTarget?.id}.`);
1314
1347
  await pourTarget?.cancelPour();
1315
1348
  onPourCancel?.();
1316
1349
  }, [onPourCancel, pourTarget]);
@@ -1338,98 +1371,6 @@ const usePourTarget = ({
1338
1371
  status: pourTarget
1339
1372
  };
1340
1373
  };
1341
- const logger$2 = KosLog.createLogger({
1342
- name: "TapOrHoldIntentStrategy",
1343
- group: "kos-hooks"
1344
- });
1345
- class TapOrHoldIntentStrategy {
1346
- intent;
1347
- name = "TapOrHoldIntentStrategy";
1348
- isHold = false;
1349
- disposer = null;
1350
- constructor(intent) {
1351
- this.intent = intent;
1352
- }
1353
- async initiate(model, intent) {
1354
- this.disposer = setTimeout(() => {
1355
- this.isHold = true;
1356
- }, 250);
1357
- if (model?.isPouring && !this.isHold) {
1358
- logger$2.debug(`additional tap detected. cancelling pour`);
1359
- await model.cancelPour();
1360
- return;
1361
- }
1362
- if (!model?.canPour) {
1363
- logger$2.debug(`${model?.id} cannot pour. Returning early.`);
1364
- return;
1365
- }
1366
- if (intent) {
1367
- logger$2.debug(`performing intent: ${intent}`);
1368
- await model?.performIntent(intent);
1369
- } else {
1370
- logger$2.debug("no intent provided. Returning early.");
1371
- }
1372
- }
1373
- async initiateEnded(nozzle) {
1374
- if (this.disposer) clearTimeout(this.disposer);
1375
- this.disposer = null;
1376
- if (!this.isHold) {
1377
- logger$2.debug("Tap detected. don't cancel and return.");
1378
- return true;
1379
- }
1380
- await nozzle?.cancelPour();
1381
- return false;
1382
- }
1383
- }
1384
- const logger$1 = KosLog.createLogger({
1385
- name: "TapToPourIntentStrategy",
1386
- group: "kos-hooks"
1387
- });
1388
- const TapToPourIntentStrategy = {
1389
- name: "TapToPourIntentStrategy",
1390
- async initiate(model, intent) {
1391
- if (model?.isPouring) {
1392
- logger$1.debug(`Nozzle ${model?.id} is already pouring. cancelling pour`);
1393
- await model.cancelPour();
1394
- return;
1395
- }
1396
- if (!model?.canPour) {
1397
- logger$1.debug(`Nozzle ${model?.id} cannot pour. Returning early.`);
1398
- return;
1399
- }
1400
- if (intent) {
1401
- logger$1.debug(`performing intent: ${intent}`);
1402
- await model?.performIntent(intent);
1403
- } else {
1404
- logger$1.debug("no intent provided. Returning early.");
1405
- }
1406
- },
1407
- async initiateEnded(_nozzle) {
1408
- return true;
1409
- }
1410
- };
1411
- const logger = KosLog.createLogger({
1412
- name: "HoldToPourStrategy",
1413
- group: "kos-hooks"
1414
- });
1415
- const TapToPourStrategy = {
1416
- name: "TapToPourStrategy",
1417
- async initiate(nozzle) {
1418
- if (nozzle?.isPouring) {
1419
- logger.debug(`Nozzle ${nozzle?.id} is already pouring. cancelling pour`);
1420
- await nozzle.cancelPour();
1421
- return;
1422
- }
1423
- if (!nozzle?.canPour) {
1424
- logger.debug(`Nozzle ${nozzle?.id} cannot pour. Returning early.`);
1425
- return;
1426
- }
1427
- await nozzle?.pour();
1428
- },
1429
- async initiateEnded(_nozzle) {
1430
- return true;
1431
- }
1432
- };
1433
1374
  const useGenericPour = (id, servicePathFactory, itemId, data) => {
1434
1375
  const modelId = id;
1435
1376
  const result = useKosModel({
@@ -1567,7 +1508,19 @@ function createDispenseKitContext() {
1567
1508
  }
1568
1509
  return ctx;
1569
1510
  };
1570
- return [useCuiKitContext, CuiKitContext.Provider];
1511
+ const useCuiPour = (options = {}) => {
1512
+ const kit = useContext(CuiKitContext);
1513
+ const gesture = usePressGesture({
1514
+ onPressed: () => void kit?.pour.press(options),
1515
+ onRelease: () => kit?.pour.release(options)
1516
+ });
1517
+ return {
1518
+ handlers: gesture.dom,
1519
+ start: async () => await kit?.pour.start(options) ?? false,
1520
+ cancel: () => kit?.pour.cancel()
1521
+ };
1522
+ };
1523
+ return [useCuiKitContext, CuiKitContext.Provider, useCuiPour];
1571
1524
  }
1572
1525
  function CuiKitWrapper({
1573
1526
  children,
@@ -1616,10 +1569,11 @@ function CuiKit({
1616
1569
  ) }) : null;
1617
1570
  }
1618
1571
  function initializeCuiKit(props) {
1619
- const [useCuiKitContext, DispenseKitProvider] = createDispenseKitContext();
1572
+ const [useCuiKitContext, DispenseKitProvider, useCuiPour] = createDispenseKitContext();
1620
1573
  return [
1621
1574
  useCuiKitContext,
1622
- CuiKit({ provider: DispenseKitProvider, props })
1575
+ CuiKit({ provider: DispenseKitProvider, props }),
1576
+ useCuiPour
1623
1577
  ];
1624
1578
  }
1625
1579
  const Holder = createContext(void 0);
@@ -1732,47 +1686,46 @@ const usePumpContext = () => {
1732
1686
  return context;
1733
1687
  };
1734
1688
  export {
1735
- usePourTarget as $,
1689
+ usePumpContext as $,
1736
1690
  AssemblyProvider as A,
1737
1691
  BoardContainerProvider as B,
1738
1692
  CuiKit as C,
1739
1693
  DISPENSE_GROUP_BEVERAGE as D,
1740
- useDefaultCuiKitLifecycle as E,
1741
- useDeviceAssembly as F,
1742
- useDeviceAssemblyContext as G,
1694
+ useDeviceAssembly as E,
1695
+ useDeviceAssemblyContext as F,
1696
+ useDispenseLifecycle as G,
1743
1697
  HoldToPourIntentStrategy as H,
1744
1698
  IngredientContainerProvider as I,
1745
- useDispenseLifecycle as J,
1746
- useFlavors as K,
1747
- useGenericPour as L,
1748
- useHolder as M,
1699
+ useFlavors as J,
1700
+ useGenericPour as K,
1701
+ useHolder as L,
1702
+ useHolderContainer as M,
1749
1703
  NozzleProvider as N,
1750
- useHolderContainer as O,
1751
- PourContext as P,
1752
- useHolderContainerContext as Q,
1753
- useHolderContext as R,
1754
- useIngredient as S,
1704
+ useHolderContainerContext as O,
1705
+ PumpProvider as P,
1706
+ useHolderContext as Q,
1707
+ useIngredient as R,
1708
+ useIngredientContainer as S,
1755
1709
  TapOrHoldIntentStrategy as T,
1756
- useIngredientContainer as U,
1757
- useIngredientContainerContext as V,
1758
- useIngredientContext as W,
1759
- useIngredientPour as X,
1760
- useNozzle as Y,
1761
- useNozzleContext as Z,
1762
- useNozzlePour as _,
1710
+ useIngredientContainerContext as U,
1711
+ useIngredientContext as V,
1712
+ useIngredientPour as W,
1713
+ useNozzle as X,
1714
+ useNozzleContext as Y,
1715
+ useNozzlePour as Z,
1716
+ usePourTarget as _,
1763
1717
  AvailabilityProvider as a,
1764
- usePumpContext as a0,
1765
- withAssembly as a1,
1766
- withBeverage as a2,
1767
- withBoard as a3,
1768
- withBoardContainer as a4,
1769
- withDeviceAssembly as a5,
1770
- withGenericPour as a6,
1771
- withHolder as a7,
1772
- withHolderContainer as a8,
1773
- withIngredient as a9,
1774
- withIngredientContainer as aa,
1775
- withNozzle as ab,
1718
+ withAssembly as a0,
1719
+ withBeverage as a1,
1720
+ withBoard as a2,
1721
+ withBoardContainer as a3,
1722
+ withDeviceAssembly as a4,
1723
+ withGenericPour as a5,
1724
+ withHolder as a6,
1725
+ withHolderContainer as a7,
1726
+ withIngredient as a8,
1727
+ withIngredientContainer as a9,
1728
+ withNozzle as aa,
1776
1729
  BoardProvider as b,
1777
1730
  CuiKitWrapper as c,
1778
1731
  DISPENSE_GROUP_BRAND as d,
@@ -1782,21 +1735,21 @@ export {
1782
1735
  HolderContainerProvider as h,
1783
1736
  HolderProvider as i,
1784
1737
  IngredientProvider as j,
1785
- PumpProvider as k,
1786
- TapToPourIntentStrategy as l,
1787
- TapToPourStrategy as m,
1788
- initializeCuiKit as n,
1789
- useAssemblyContext as o,
1790
- useAvailability as p,
1791
- useAvailabilityContext as q,
1792
- useBeverages as r,
1793
- useBoard as s,
1794
- useBoardContainer as t,
1738
+ TapToPourIntentStrategy as k,
1739
+ TapToPourStrategy as l,
1740
+ initializeCuiKit as m,
1741
+ useAssemblyContext as n,
1742
+ useAvailability as o,
1743
+ useAvailabilityContext as p,
1744
+ useBeverages as q,
1745
+ useBoard as r,
1746
+ useBoardContainer as s,
1747
+ useBoardContainerContext as t,
1795
1748
  useAssembly as u,
1796
- useBoardContainerContext as v,
1797
- useBoardContext as w,
1798
- useBrands as x,
1799
- useCuiKit as y,
1800
- useCuiKitTimers as z
1749
+ useBoardContext as v,
1750
+ useBrands as w,
1751
+ useCuiKit as x,
1752
+ useCuiKitTimers as y,
1753
+ useDefaultCuiKitLifecycle as z
1801
1754
  };
1802
- //# sourceMappingURL=pump-provider-BQmCKz7S.js.map
1755
+ //# sourceMappingURL=pump-provider-Ch7NOeRj.js.map