@recipe-book/react 0.1.0 → 0.1.1

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.
package/dist/index.cjs CHANGED
@@ -66,12 +66,9 @@ __export(index_exports, {
66
66
  });
67
67
  module.exports = __toCommonJS(index_exports);
68
68
 
69
- // src/parts/image.tsx
70
- var import_react4 = require("react");
71
-
72
69
  // src/context.tsx
73
70
  var import_core3 = require("@recipe-book/core");
74
- var import_react3 = require("react");
71
+ var import_react4 = require("react");
75
72
 
76
73
  // src/labels.ts
77
74
  var defaultLabels = {
@@ -85,9 +82,12 @@ var defaultLabels = {
85
82
  to: "to"
86
83
  };
87
84
 
85
+ // src/react.ts
86
+ var import_react = require("react");
87
+
88
88
  // src/state/use-recipe-state.ts
89
89
  var import_core2 = require("@recipe-book/core");
90
- var import_react2 = require("react");
90
+ var import_react3 = require("react");
91
91
 
92
92
  // src/is-parsed-recipe.ts
93
93
  var import_core = require("@recipe-book/core");
@@ -128,46 +128,45 @@ function quote(text) {
128
128
  }
129
129
 
130
130
  // src/state/use-controllable-state.ts
131
- var import_react = require("react");
132
- function useControllableState({
133
- prop,
134
- value,
135
- defaultValue,
136
- onChange,
137
- resetKey
138
- }) {
139
- const controlled = value !== void 0;
140
- const [internal, setInternal] = (0, import_react.useState)(defaultValue);
141
- const [internalResetKey, setInternalResetKey] = (0, import_react.useState)(resetKey);
131
+ var import_react2 = require("react");
132
+ function useControllableState(options) {
133
+ const { value, defaultValue, onChange, resetKey } = options;
134
+ const [internal, setInternal] = (0, import_react2.useState)(defaultValue);
135
+ const [internalResetKey, setInternalResetKey] = (0, import_react2.useState)(resetKey);
142
136
  let uncontrolled = internal;
143
137
  if (!Object.is(resetKey, internalResetKey)) {
144
138
  setInternalResetKey(resetKey);
145
139
  setInternal(defaultValue);
146
140
  uncontrolled = defaultValue;
147
141
  }
148
- const current = controlled ? value : uncontrolled;
149
- const latest = (0, import_react.useRef)({ controlled, value: current, onChange });
150
- (0, import_react.useInsertionEffect)(() => {
151
- latest.current = { controlled, value: current, onChange };
142
+ const current = value === void 0 ? uncontrolled : value;
143
+ const latest = (0, import_react2.useRef)({ value, current, onChange });
144
+ (0, import_react2.useInsertionEffect)(() => {
145
+ latest.current = { value, current, onChange };
152
146
  });
153
- const setValue = (0, import_react.useCallback)((next) => {
154
- const { controlled: controlled2, value: value2, onChange: onChange2 } = latest.current;
155
- if (Object.is(next, value2)) return;
156
- if (!controlled2) {
157
- latest.current.value = next;
147
+ const setValue = (0, import_react2.useCallback)((next) => {
148
+ const { value: value2, current: current2, onChange: onChange2 } = latest.current;
149
+ if (Object.is(next, current2)) return;
150
+ if (value2 === void 0) {
151
+ latest.current.current = next;
158
152
  setInternal(next);
159
153
  }
160
154
  onChange2?.(next);
161
155
  }, []);
162
156
  if (process.env.NODE_ENV !== "production") {
163
- useModeWarnings(prop, controlled, controlled && onChange === void 0);
157
+ const controlled = value !== void 0;
158
+ useModeWarnings(
159
+ options.prop,
160
+ controlled,
161
+ controlled && onChange === void 0
162
+ );
164
163
  }
165
164
  return [current, setValue];
166
165
  }
167
166
  function useModeWarnings(prop, controlled, readOnly) {
168
- const initiallyControlled = (0, import_react.useRef)(controlled);
169
- const warned2 = (0, import_react.useRef)({ readOnly: false, modeSwitch: false });
170
- (0, import_react.useEffect)(() => {
167
+ const initiallyControlled = (0, import_react2.useRef)(controlled);
168
+ const warned2 = (0, import_react2.useRef)({ readOnly: false, modeSwitch: false });
169
+ (0, import_react2.useEffect)(() => {
171
170
  const name = prop.charAt(0).toUpperCase() + prop.slice(1);
172
171
  if (readOnly && !warned2.current.readOnly) {
173
172
  warned2.current.readOnly = true;
@@ -189,11 +188,11 @@ function useModeWarnings(prop, controlled, readOnly) {
189
188
  function useRecipeState(options) {
190
189
  const { recipe } = options;
191
190
  if (process.env.NODE_ENV !== "production") {
192
- (0, import_react2.useMemo)(() => {
191
+ (0, import_react3.useMemo)(() => {
193
192
  assertParsedRecipe(
194
193
  recipe,
195
194
  "useRecipeState()",
196
- "Call parseRecipe() from @recipe-book/core and pass result.data."
195
+ "Call parseRecipe() from @recipe-book/react/core and pass result.data."
197
196
  );
198
197
  }, [recipe]);
199
198
  }
@@ -227,7 +226,7 @@ function useRecipeState(options) {
227
226
  system !== rawSystem && `Recipe Book: \`system\` must be "metric" or "us", received ${describe(rawSystem)}. Showing "${system}" instead.`
228
227
  );
229
228
  }
230
- const setServings = (0, import_react2.useCallback)(
229
+ const setServings = (0, import_react3.useCallback)(
231
230
  (next) => {
232
231
  if (isServingsFor(next, base)) {
233
232
  updateServings(next);
@@ -239,7 +238,7 @@ function useRecipeState(options) {
239
238
  },
240
239
  [updateServings, base]
241
240
  );
242
- const setSystem = (0, import_react2.useCallback)(
241
+ const setSystem = (0, import_react3.useCallback)(
243
242
  (next) => {
244
243
  if (isSystem(next)) {
245
244
  updateSystem(next);
@@ -251,66 +250,64 @@ function useRecipeState(options) {
251
250
  },
252
251
  [updateSystem]
253
252
  );
254
- const steps = (0, import_react2.useMemo)(() => numberSteps(recipe), [recipe]);
255
- const { scale, canConvert, ingredients } = (0, import_react2.useMemo)(
253
+ const steps = (0, import_react3.useMemo)(() => (0, import_core2.resolveRecipe)(recipe).steps, [recipe]);
254
+ const resolved = (0, import_react3.useMemo)(
256
255
  () => (0, import_core2.resolveRecipe)(recipe, { servings, system }),
257
256
  [recipe, servings, system]
258
257
  );
259
- return (0, import_react2.useMemo)(
258
+ return (0, import_react3.useMemo)(
260
259
  () => ({
261
260
  recipe,
262
261
  servings,
263
262
  setServings,
264
- scale,
263
+ scale: resolved.scale,
265
264
  system,
266
265
  setSystem,
267
- canConvert,
268
- ingredients,
266
+ canConvert: resolved.canConvert,
267
+ ingredients: resolved.ingredients,
269
268
  steps
270
269
  }),
271
- [
272
- recipe,
273
- servings,
274
- setServings,
275
- scale,
276
- system,
277
- setSystem,
278
- canConvert,
279
- ingredients,
280
- steps
281
- ]
270
+ [recipe, servings, setServings, resolved, system, setSystem, steps]
282
271
  );
283
272
  }
284
273
  function useWarning(message) {
285
- const warned2 = (0, import_react2.useRef)(false);
286
- (0, import_react2.useEffect)(() => {
274
+ const warned2 = (0, import_react3.useRef)(false);
275
+ (0, import_react3.useEffect)(() => {
287
276
  if (message !== warned2.current) {
288
277
  warned2.current = message;
289
278
  if (message) console.warn(message);
290
279
  }
291
280
  }, [message]);
292
281
  }
293
- function numberSteps(recipe) {
294
- let number = 0;
295
- return recipe.steps.map((section) => ({
296
- ...section,
297
- items: section.items.map((step) => ({ ...step, number: ++number }))
298
- }));
299
- }
300
282
  function isServings(value) {
301
283
  return typeof value === "number" && Number.isFinite(value) && value > 0;
302
284
  }
303
285
  function servingsBase(recipe) {
304
286
  return [recipe.servings?.amount, recipe.yield?.amount].find(isServings) ?? 1;
305
287
  }
306
- var MIN_SCALE = 1e-6;
307
- var MAX_SCALE = 1e6;
288
+ function servingsWindow(base) {
289
+ const [mantissa, exponent] = base.toExponential().split("e");
290
+ return [
291
+ Number(`${mantissa}e${Number(exponent) - 6}`),
292
+ Number(`${mantissa}e${Number(exponent) + 6}`)
293
+ ];
294
+ }
308
295
  function isServingsFor(value, base) {
309
- return isServings(value) && value / base >= MIN_SCALE && value / base <= MAX_SCALE;
296
+ const [min, max] = servingsWindow(base);
297
+ return isServings(value) && value >= min && value <= max;
310
298
  }
311
299
  function describeRange(base) {
312
- const bound = (value) => String(Number(value.toPrecision(6)));
313
- return `a number from ${bound(base * MIN_SCALE)} to ${bound(base * MAX_SCALE)} for this recipe`;
300
+ const [min, max] = servingsWindow(base);
301
+ return `a number from ${roundInward(min, true)} to ${roundInward(max, false)} for this recipe`;
302
+ }
303
+ function roundInward(bound, up) {
304
+ const nearest = Number(bound.toPrecision(6));
305
+ if (up ? nearest >= bound : nearest <= bound) {
306
+ return nearest;
307
+ }
308
+ const [mantissa, exponent] = bound.toExponential().split("e");
309
+ const digits = Number(mantissa.replace(".", "").slice(0, 6));
310
+ return Number(`${up ? digits + 1 : digits}e${Number(exponent) - 5}`);
314
311
  }
315
312
  function isSystem(value) {
316
313
  return value === "metric" || value === "us";
@@ -330,7 +327,7 @@ function describe(value) {
330
327
 
331
328
  // src/context.tsx
332
329
  var import_jsx_runtime = require("react/jsx-runtime");
333
- var ConfigContext = /* @__PURE__ */ (0, import_react3.createContext)({
330
+ var ConfigContext = /* @__PURE__ */ (0, import_react.createContext)({
334
331
  locale: "en-US",
335
332
  labels: defaultLabels
336
333
  });
@@ -339,7 +336,7 @@ function RecipeBookProvider({
339
336
  ...config
340
337
  }) {
341
338
  const { locale, labels, defaultSystem } = useConfig(config);
342
- const value = (0, import_react3.useMemo)(
339
+ const value = (0, import_react4.useMemo)(
343
340
  () => ({ locale, labels, defaultSystem }),
344
341
  [locale, labels, defaultSystem]
345
342
  );
@@ -350,10 +347,10 @@ function useConfig({
350
347
  labels,
351
348
  defaultSystem
352
349
  }) {
353
- const outer = (0, import_react3.useContext)(ConfigContext);
350
+ const outer = (0, import_react4.useContext)(ConfigContext);
354
351
  return {
355
352
  locale: locale ?? outer.locale,
356
- labels: (0, import_react3.useMemo)(
353
+ labels: (0, import_react4.useMemo)(
357
354
  () => ({
358
355
  ...outer.labels,
359
356
  ...Object.fromEntries(
@@ -364,13 +361,13 @@ function useConfig({
364
361
  }),
365
362
  [outer.labels, labels]
366
363
  ),
367
- defaultSystem: isSystem(defaultSystem) ? defaultSystem : outer.defaultSystem
364
+ defaultSystem: [defaultSystem, outer.defaultSystem].find(isSystem)
368
365
  };
369
366
  }
370
- var RecipeDataContext = /* @__PURE__ */ (0, import_react3.createContext)(null);
371
- var RecipeStateContext = /* @__PURE__ */ (0, import_react3.createContext)(null);
367
+ var RecipeDataContext = /* @__PURE__ */ (0, import_react.createContext)(null);
368
+ var RecipeStateContext = /* @__PURE__ */ (0, import_react.createContext)(null);
372
369
  function useRequired(context, message) {
373
- const value = (0, import_react3.useContext)(context);
370
+ const value = (0, import_react4.useContext)(context);
374
371
  if (!value) {
375
372
  throw new import_core3.RecipeBookError("missing_parent", message);
376
373
  }
@@ -397,17 +394,14 @@ function useRecipe() {
397
394
 
398
395
  // src/parts/image.tsx
399
396
  var import_jsx_runtime2 = require("react/jsx-runtime");
400
- var Image = /* @__PURE__ */ (0, import_react4.forwardRef)(function Image2({
397
+ var Image = /* @__PURE__ */ (0, import_react.forwardRef)(function Image2({
401
398
  as: Comp = "img",
402
399
  src,
403
400
  alt,
404
401
  ...props
405
402
  }, ref) {
406
403
  const { recipe } = useRecipeData("Recipe.Image");
407
- if (recipe.image === void 0) {
408
- return null;
409
- }
410
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
404
+ return recipe.image !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
411
405
  Comp,
412
406
  {
413
407
  ...props,
@@ -420,16 +414,16 @@ var Image = /* @__PURE__ */ (0, import_react4.forwardRef)(function Image2({
420
414
  });
421
415
 
422
416
  // src/parts/ingredients.tsx
423
- var import_react8 = require("react");
417
+ var import_react12 = require("react");
424
418
 
425
419
  // src/slot.tsx
426
- var import_react5 = require("react");
420
+ var import_react7 = require("react");
427
421
  function resolveAsChild(part, tag, asChild, children, defaults) {
428
422
  if (asChild) {
429
- if ((0, import_react5.isValidElement)(children) && children.type !== import_react5.Fragment) {
423
+ if ((0, import_react.isValidElement)(children) && children.type !== import_react.Fragment) {
430
424
  return [
431
425
  Slot,
432
- isEmpty(children.props.children) ? (0, import_react5.cloneElement)(children, void 0, defaults) : children
426
+ isEmpty(children.props.children) ? (0, import_react.cloneElement)(children, void 0, defaults) : children
433
427
  ];
434
428
  }
435
429
  if (process.env.NODE_ENV !== "production" && !warned.has(part)) {
@@ -441,7 +435,7 @@ function resolveAsChild(part, tag, asChild, children, defaults) {
441
435
  }
442
436
  return [tag, isEmpty(children) ? defaults : children];
443
437
  }
444
- var Slot = /* @__PURE__ */ (0, import_react5.forwardRef)(function Slot2({ children, ...props }, ref) {
438
+ var Slot = /* @__PURE__ */ (0, import_react.forwardRef)(function Slot2({ children, ...props }, ref) {
445
439
  const child = children;
446
440
  const merged = { ...props, ...child.props };
447
441
  for (const name in child.props) {
@@ -462,7 +456,7 @@ var Slot = /* @__PURE__ */ (0, import_react5.forwardRef)(function Slot2({ childr
462
456
  }
463
457
  }
464
458
  const childRef = Object.getOwnPropertyDescriptor(child.props, "ref")?.value ?? child.ref;
465
- merged.ref = (0, import_react5.useCallback)(
459
+ merged.ref = (0, import_react7.useCallback)(
466
460
  (node) => {
467
461
  const refs = [ref, childRef];
468
462
  const cleanups = refs.map((each) => setRef(each, node));
@@ -480,7 +474,7 @@ var Slot = /* @__PURE__ */ (0, import_react5.forwardRef)(function Slot2({ childr
480
474
  },
481
475
  [ref, childRef]
482
476
  );
483
- return (0, import_react5.cloneElement)(child, merged);
477
+ return (0, import_react.cloneElement)(child, merged);
484
478
  });
485
479
  function setRef(ref, node) {
486
480
  if (typeof ref === "function") {
@@ -493,7 +487,7 @@ function setRef(ref, node) {
493
487
  }
494
488
  var warned = /* @__PURE__ */ new Set();
495
489
  function describeChildren(children) {
496
- if ((0, import_react5.isValidElement)(children)) {
490
+ if ((0, import_react.isValidElement)(children)) {
497
491
  return "a Fragment";
498
492
  }
499
493
  if (Array.isArray(children)) {
@@ -511,25 +505,26 @@ function isEmpty(children) {
511
505
 
512
506
  // src/use-format-ingredient.ts
513
507
  var import_core4 = require("@recipe-book/core");
514
- var import_react6 = require("react");
508
+ var import_react9 = require("react");
515
509
  function useFormatIngredient() {
516
510
  const {
517
511
  locale,
518
512
  labels: { optional }
519
513
  } = useRequired(
520
514
  RecipeDataContext,
521
- "useFormatIngredient() must be called inside <Recipe.Root>. To format ingredients without a Root, call formatIngredient() from @recipe-book/core."
515
+ "useFormatIngredient() must be called inside <Recipe.Root>. To format ingredients without a Root, call formatIngredient() from @recipe-book/react/core."
522
516
  );
523
- return (0, import_react6.useCallback)(
517
+ return (0, import_react9.useCallback)(
524
518
  (item) => formatLine(item, locale, optional),
525
519
  [locale, optional]
526
520
  );
527
521
  }
528
522
  function formatLine(item, locale, optional) {
529
523
  const options = { locale };
524
+ const formatted = (0, import_core4.formatIngredient)(item, options);
530
525
  const quantity = lineQuantity(item);
531
526
  return {
532
- ...(0, import_core4.formatIngredient)(item, options),
527
+ ...formatted,
533
528
  quantityParts: quantity ? (0, import_core4.formatQuantityToParts)(quantity, options) : [],
534
529
  ...item.ingredient.optional && { optional }
535
530
  };
@@ -539,10 +534,10 @@ function lineQuantity(item) {
539
534
  }
540
535
 
541
536
  // src/parts/sections.tsx
542
- var import_react7 = require("react");
537
+ var import_react10 = require("react");
543
538
  var import_jsx_runtime3 = require("react/jsx-runtime");
544
539
  function renderKeyed(items, render) {
545
- return items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react7.Fragment, { children: render(item) }, item.id));
540
+ return items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react.Fragment, { children: render(item) }, item.id));
546
541
  }
547
542
  function SectionBlock({
548
543
  kind,
@@ -550,21 +545,18 @@ function SectionBlock({
550
545
  headingLevel,
551
546
  children
552
547
  }) {
553
- const headingId = (0, import_react7.useId)();
548
+ const headingId = (0, import_react10.useId)();
554
549
  const marker = `data-recipe-${kind}-section`;
555
- if (!title) {
556
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ...{ [marker]: "" }, children });
557
- }
558
550
  const Heading = `h${headingLevel}`;
559
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { ...{ [marker]: "" }, "aria-labelledby": headingId, children: [
551
+ return title ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { ...{ [marker]: "" }, "aria-labelledby": headingId, children: [
560
552
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Heading, { id: headingId, children: title }),
561
553
  children
562
- ] });
554
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ...{ [marker]: "" }, children });
563
555
  }
564
556
 
565
557
  // src/parts/ingredients.tsx
566
558
  var import_jsx_runtime4 = require("react/jsx-runtime");
567
- var Ingredients = /* @__PURE__ */ (0, import_react8.forwardRef)(
559
+ var Ingredients = /* @__PURE__ */ (0, import_react.forwardRef)(
568
560
  function Ingredients2({ headingLevel = 3, children, ...props }, ref) {
569
561
  const { ingredients } = usePartState("Recipe.Ingredients");
570
562
  const renderSection = children ?? ((section) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
@@ -579,21 +571,21 @@ var Ingredients = /* @__PURE__ */ (0, import_react8.forwardRef)(
579
571
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { ...props, "data-recipe-ingredients": "", ref, children: renderKeyed(ingredients, renderSection) });
580
572
  }
581
573
  );
582
- var IngredientList = /* @__PURE__ */ (0, import_react8.forwardRef)(
574
+ var IngredientList = /* @__PURE__ */ (0, import_react.forwardRef)(
583
575
  function IngredientList2({ section, children = (item) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Ingredient, { item }), ...props }, ref) {
584
576
  const { ingredients } = usePartState("Recipe.IngredientList");
585
577
  const items = section?.items ?? ingredients.flatMap((s) => s.items);
586
578
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { ...props, "data-recipe-ingredient-list": "", ref, children: renderKeyed(items, children) });
587
579
  }
588
580
  );
589
- var LineContext = /* @__PURE__ */ (0, import_react8.createContext)(null);
590
- var Ingredient = /* @__PURE__ */ (0, import_react8.forwardRef)(
581
+ var LineContext = /* @__PURE__ */ (0, import_react.createContext)(null);
582
+ var Ingredient = /* @__PURE__ */ (0, import_react.forwardRef)(
591
583
  function Ingredient2({ item, asChild = false, children, ...props }, ref) {
592
584
  const {
593
585
  labels: { optional, to },
594
586
  locale
595
587
  } = useRecipeData("Recipe.Ingredient");
596
- const line = (0, import_react8.useMemo)(
588
+ const line = (0, import_react12.useMemo)(
597
589
  () => ({ ...formatLine(item, locale, optional), to }),
598
590
  [item, locale, optional, to]
599
591
  );
@@ -642,12 +634,12 @@ function ingredientText(name, attribute, read) {
642
634
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Comp, { ...props, ...{ [attribute]: "" }, ref, children: Comp === "span" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
643
635
  children,
644
636
  text
645
- ] }) : (0, import_react8.cloneElement)(child, void 0, child.props.children, text) });
637
+ ] }) : (0, import_react.cloneElement)(child, void 0, child.props.children, text) });
646
638
  }
647
639
  if (process.env.NODE_ENV !== "production") {
648
640
  Object.defineProperty(IngredientText, "name", { value: name });
649
641
  }
650
- return (0, import_react8.forwardRef)(IngredientText);
642
+ return (0, import_react.forwardRef)(IngredientText);
651
643
  }
652
644
  var IngredientQuantity = /* @__PURE__ */ ingredientText(
653
645
  "IngredientQuantity",
@@ -701,9 +693,9 @@ var IngredientOptional = /* @__PURE__ */ ingredientText(
701
693
 
702
694
  // src/parts/json-ld.tsx
703
695
  var import_schema_org = require("@recipe-book/core/schema-org");
704
- var import_react9 = require("react");
696
+ var import_react14 = require("react");
705
697
  var import_jsx_runtime5 = require("react/jsx-runtime");
706
- var JsonLd = /* @__PURE__ */ (0, import_react9.forwardRef)(
698
+ var JsonLd = /* @__PURE__ */ (0, import_react.forwardRef)(
707
699
  function JsonLd2({
708
700
  servings,
709
701
  system,
@@ -715,12 +707,12 @@ var JsonLd = /* @__PURE__ */ (0, import_react9.forwardRef)(
715
707
  ...props
716
708
  }, ref) {
717
709
  const { recipe } = useRecipeData("Recipe.JsonLd");
718
- const { data, warnings } = (0, import_react9.useMemo)(
710
+ const result = (0, import_react14.useMemo)(
719
711
  () => (0, import_schema_org.toJsonLd)(recipe, { servings, system, url, extra }),
720
712
  [recipe, servings, system, url, extra]
721
713
  );
722
714
  if (process.env.NODE_ENV !== "production") {
723
- useWarningsReport(recipe.name, warnings);
715
+ useWarningsReport(recipe.name, result.warnings);
724
716
  }
725
717
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
726
718
  "script",
@@ -729,7 +721,7 @@ var JsonLd = /* @__PURE__ */ (0, import_react9.forwardRef)(
729
721
  type: "application/ld+json",
730
722
  "data-recipe-json-ld": "",
731
723
  dangerouslySetInnerHTML: {
732
- __html: JSON.stringify(data).replace(/</g, "\\u003c")
724
+ __html: JSON.stringify(result.data).replace(/</g, "\\u003c")
733
725
  },
734
726
  ref
735
727
  }
@@ -737,8 +729,8 @@ var JsonLd = /* @__PURE__ */ (0, import_react9.forwardRef)(
737
729
  }
738
730
  );
739
731
  function useWarningsReport(name, warnings) {
740
- const reported = (0, import_react9.useRef)(void 0);
741
- (0, import_react9.useEffect)(() => {
732
+ const reported = (0, import_react14.useRef)(void 0);
733
+ (0, import_react14.useEffect)(() => {
742
734
  const key = JSON.stringify(warnings);
743
735
  const count = warnings.length;
744
736
  if (count > 0 && key !== reported.current) {
@@ -752,7 +744,7 @@ function useWarningsReport(name, warnings) {
752
744
  }
753
745
 
754
746
  // src/parts/root.tsx
755
- var import_react10 = require("react");
747
+ var import_react16 = require("react");
756
748
  var import_jsx_runtime6 = require("react/jsx-runtime");
757
749
  function Root({
758
750
  recipe,
@@ -761,14 +753,14 @@ function Root({
761
753
  children,
762
754
  ...options
763
755
  }) {
764
- const result = (0, import_react10.useMemo)(() => {
756
+ const result = (0, import_react16.useMemo)(() => {
765
757
  if (parse !== void 0) {
766
758
  return parse(recipe);
767
759
  }
768
760
  assertParsedRecipe(
769
761
  recipe,
770
762
  "<Recipe.Root>",
771
- "Call parseRecipe() from @recipe-book/core on the server, in a loader, or at build time and pass result.data, or pass parse={parseRecipe} to parse it in the browser."
763
+ "Call parseRecipe() from @recipe-book/react/core on the server, in a loader, or at build time and pass result.data, or pass parse={parseRecipe} to parse it in the browser."
772
764
  );
773
765
  return { success: true, data: recipe, warnings: [] };
774
766
  }, [parse, recipe]);
@@ -783,23 +775,23 @@ function Root({
783
775
  function RecipeProvider({ children, ...options }) {
784
776
  const { locale, labels, defaultSystem } = useConfig(options);
785
777
  const state = useRecipeState({ ...options, defaultSystem });
786
- const [temperatures, setTemperatures] = (0, import_react10.useState)(0);
787
- const reportTemperature = (0, import_react10.useCallback)(() => {
778
+ const [temperatures, setTemperatures] = (0, import_react16.useState)(0);
779
+ const reportTemperature = (0, import_react16.useCallback)(() => {
788
780
  setTemperatures((count) => count + 1);
789
781
  return () => setTemperatures((count) => count - 1);
790
782
  }, []);
791
783
  const canConvert = state.canConvert || temperatures > 0;
792
- const value = (0, import_react10.useMemo)(() => ({ ...state, canConvert }), [state, canConvert]);
784
+ const value = (0, import_react16.useMemo)(() => ({ ...state, canConvert }), [state, canConvert]);
793
785
  const { recipe, steps } = state;
794
- const data = (0, import_react10.useMemo)(
786
+ const data = (0, import_react16.useMemo)(
795
787
  () => ({ recipe, steps, labels, locale, reportTemperature }),
796
788
  [recipe, steps, labels, locale, reportTemperature]
797
789
  );
798
790
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RecipeDataContext.Provider, { value: data, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RecipeStateContext.Provider, { value, children }) });
799
791
  }
800
792
  function useParseReport(result) {
801
- const reported = (0, import_react10.useRef)(void 0);
802
- (0, import_react10.useEffect)(() => {
793
+ const reported = (0, import_react16.useRef)(void 0);
794
+ (0, import_react16.useEffect)(() => {
803
795
  const key = result.success ? result.warnings.length > 0 ? `warnings ${JSON.stringify(result.warnings)}` : void 0 : `error ${JSON.stringify([result.error.message, result.error.issues])}`;
804
796
  if (key === reported.current) {
805
797
  return;
@@ -822,7 +814,7 @@ function useParseReport(result) {
822
814
 
823
815
  // src/parts/servings.tsx
824
816
  var import_core5 = require("@recipe-book/core");
825
- var import_react11 = require("react");
817
+ var import_react17 = require("react");
826
818
 
827
819
  // src/compose-event-handlers.ts
828
820
  function composeEventHandlers(theirs, ours) {
@@ -836,7 +828,7 @@ function composeEventHandlers(theirs, ours) {
836
828
 
837
829
  // src/parts/servings.tsx
838
830
  var import_jsx_runtime7 = require("react/jsx-runtime");
839
- var ServingsSelect = /* @__PURE__ */ (0, import_react11.forwardRef)(
831
+ var ServingsSelect = /* @__PURE__ */ (0, import_react.forwardRef)(
840
832
  function ServingsSelect2({ options, "aria-label": ariaLabel, onChange, ...props }, ref) {
841
833
  const { recipe, labels, locale } = useRecipeData("Recipe.ServingsSelect");
842
834
  const { servings, setServings } = usePartState("Recipe.ServingsSelect");
@@ -864,7 +856,7 @@ var ServingsSelect = /* @__PURE__ */ (0, import_react11.forwardRef)(
864
856
  );
865
857
  }
866
858
  );
867
- var ServingsInput = /* @__PURE__ */ (0, import_react11.forwardRef)(
859
+ var ServingsInput = /* @__PURE__ */ (0, import_react.forwardRef)(
868
860
  function ServingsInput2({
869
861
  min = 1,
870
862
  max,
@@ -878,7 +870,7 @@ var ServingsInput = /* @__PURE__ */ (0, import_react11.forwardRef)(
878
870
  const { recipe, labels } = useRecipeData("Recipe.ServingsInput");
879
871
  const { servings, setServings } = usePartState("Recipe.ServingsInput");
880
872
  const base = servingsBase(recipe);
881
- const [draft, setDraft] = (0, import_react11.useState)({ text: String(servings), servings });
873
+ const [draft, setDraft] = (0, import_react17.useState)({ text: String(servings), servings });
882
874
  if (draft.servings !== servings) {
883
875
  setDraft({
884
876
  text: Number(draft.text) === servings ? draft.text : String(servings),
@@ -944,12 +936,11 @@ function optionText(choice, { servings, yield: makes }, base, labels, locale) {
944
936
 
945
937
  // src/parts/step-text.tsx
946
938
  var import_core6 = require("@recipe-book/core");
947
- var import_react13 = require("react");
939
+ var import_react20 = require("react");
948
940
 
949
941
  // src/parts/steps.tsx
950
- var import_react12 = require("react");
951
942
  var import_jsx_runtime8 = require("react/jsx-runtime");
952
- var Steps = /* @__PURE__ */ (0, import_react12.forwardRef)(function Steps2({ headingLevel = 3, children, ...props }, ref) {
943
+ var Steps = /* @__PURE__ */ (0, import_react.forwardRef)(function Steps2({ headingLevel = 3, children, ...props }, ref) {
953
944
  const { steps } = useRecipeData("Recipe.Steps");
954
945
  const renderSection = children ?? ((section) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
955
946
  SectionBlock,
@@ -962,7 +953,7 @@ var Steps = /* @__PURE__ */ (0, import_react12.forwardRef)(function Steps2({ hea
962
953
  ));
963
954
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { ...props, "data-recipe-steps": "", ref, children: renderKeyed(steps, renderSection) });
964
955
  });
965
- var StepList = /* @__PURE__ */ (0, import_react12.forwardRef)(
956
+ var StepList = /* @__PURE__ */ (0, import_react.forwardRef)(
966
957
  function StepList2({ section, children = (step) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Step, { item: step }), start, ...props }, ref) {
967
958
  const { steps } = useRecipeData("Recipe.StepList");
968
959
  const items = section?.items ?? steps.flatMap((s) => s.items);
@@ -978,8 +969,8 @@ var StepList = /* @__PURE__ */ (0, import_react12.forwardRef)(
978
969
  );
979
970
  }
980
971
  );
981
- var StepContext = /* @__PURE__ */ (0, import_react12.createContext)(null);
982
- var Step = /* @__PURE__ */ (0, import_react12.forwardRef)(function Step2({ item, asChild = false, children, ...props }, ref) {
972
+ var StepContext = /* @__PURE__ */ (0, import_react.createContext)(null);
973
+ var Step = /* @__PURE__ */ (0, import_react.forwardRef)(function Step2({ item, asChild = false, children, ...props }, ref) {
983
974
  useRecipeData("Recipe.Step");
984
975
  const [Comp, content] = resolveAsChild(
985
976
  "Recipe.Step",
@@ -994,7 +985,7 @@ var Step = /* @__PURE__ */ (0, import_react12.forwardRef)(function Step2({ item,
994
985
  );
995
986
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StepContext.Provider, { value: item, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Comp, { ...props, "data-recipe-step": "", ref, children: content }) });
996
987
  });
997
- var StepName = /* @__PURE__ */ (0, import_react12.forwardRef)(
988
+ var StepName = /* @__PURE__ */ (0, import_react.forwardRef)(
998
989
  function StepName2(props, ref) {
999
990
  const step = useRequired(
1000
991
  StepContext,
@@ -1006,13 +997,13 @@ var StepName = /* @__PURE__ */ (0, import_react12.forwardRef)(
1006
997
 
1007
998
  // src/parts/step-text.tsx
1008
999
  var import_jsx_runtime9 = require("react/jsx-runtime");
1009
- var StepText = /* @__PURE__ */ (0, import_react13.forwardRef)(
1000
+ var StepText = /* @__PURE__ */ (0, import_react.forwardRef)(
1010
1001
  function StepText2(props, ref) {
1011
1002
  const step = useRequired(
1012
1003
  StepContext,
1013
1004
  "<Recipe.StepText> must be rendered inside <Recipe.Step>."
1014
1005
  );
1015
- const segments = (0, import_react13.useMemo)(() => (0, import_core6.segmentStepText)(step.text), [step.text]);
1006
+ const segments = (0, import_react20.useMemo)(() => (0, import_core6.segmentStepText)(step.text), [step.text]);
1016
1007
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { ...props, "data-recipe-step-text": "", ref, children: segments.map(
1017
1008
  (segment, index) => segment.type === "temperature" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Temperature, { segment }, index) : (
1018
1009
  // Core may add segment types in a minor release; show those as authored.
@@ -1025,44 +1016,39 @@ function Temperature({ segment }) {
1025
1016
  const { labels, locale, reportTemperature } = useRecipeData("Recipe.StepText");
1026
1017
  const unit = usePartState("Recipe.StepText").system === "us" ? "F" : "C";
1027
1018
  const readings = [segment, ...segment.alternatives];
1028
- (0, import_react13.useEffect)(reportTemperature, [reportTemperature]);
1029
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { "data-recipe-temperature": "", children: [false, true].flatMap((fan) => {
1019
+ (0, import_react20.useEffect)(reportTemperature, [reportTemperature]);
1020
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { "data-recipe-temperature": "", children: [false, true].map((fan) => {
1030
1021
  const kind = readings.filter((reading2) => reading2.fan === fan);
1031
1022
  const reading = kind.find((each) => each.temperature.unit === unit) ?? kind[0];
1032
- return reading ? [
1033
- (0, import_core6.formatTemperature)(
1034
- (0, import_core6.convertTemperature)(reading.temperature, unit),
1035
- {
1036
- locale,
1037
- rounding: reading.temperature.unit === unit ? "none" : void 0
1038
- }
1039
- ) + (fan && labels.fan ? ` ${labels.fan}` : "")
1040
- ] : [];
1041
- }).join(" / ") });
1023
+ return reading && (0, import_core6.formatTemperature)((0, import_core6.convertTemperature)(reading.temperature, unit), {
1024
+ locale,
1025
+ rounding: reading.temperature.unit === unit ? "none" : void 0
1026
+ }) + (fan && labels.fan ? ` ${labels.fan}` : "");
1027
+ }).filter(Boolean).join(" / ") });
1042
1028
  }
1043
1029
 
1044
1030
  // src/parts/system-toggle.tsx
1045
- var import_react14 = require("react");
1031
+ var import_react22 = require("react");
1046
1032
  var import_jsx_runtime10 = require("react/jsx-runtime");
1047
- var ToggleContext = /* @__PURE__ */ (0, import_react14.createContext)(
1033
+ var ToggleContext = /* @__PURE__ */ (0, import_react.createContext)(
1048
1034
  null
1049
1035
  );
1050
- var SystemToggle = /* @__PURE__ */ (0, import_react14.forwardRef)(
1036
+ var SystemToggle = /* @__PURE__ */ (0, import_react.forwardRef)(
1051
1037
  function SystemToggle2({ asChild = false, "aria-label": ariaLabel, children, ...props }, ref) {
1052
1038
  const { labels } = useRecipeData("Recipe.SystemToggle");
1053
1039
  const { system, setSystem, canConvert } = usePartState(
1054
1040
  "Recipe.SystemToggle"
1055
1041
  );
1056
1042
  const disabled = !canConvert;
1057
- const [offers, setOffers] = (0, import_react14.useState)([]);
1058
- const [chosen, choose] = (0, import_react14.useState)();
1059
- const offer = (0, import_react14.useCallback)((id, value) => {
1043
+ const [offers, setOffers] = (0, import_react22.useState)([]);
1044
+ const [chosen, choose] = (0, import_react22.useState)();
1045
+ const offer = (0, import_react22.useCallback)((id, value) => {
1060
1046
  const entry = { id, value };
1061
1047
  setOffers((current) => [...current, entry]);
1062
1048
  return () => setOffers((current) => current.filter((other) => other !== entry));
1063
1049
  }, []);
1064
1050
  const tabStop = (offers.find((entry) => entry.value === system && entry.id === chosen) ?? offers.find((entry) => entry.value === system) ?? offers[0])?.id;
1065
- const context = (0, import_react14.useMemo)(
1051
+ const context = (0, import_react22.useMemo)(
1066
1052
  () => ({ system, setSystem, disabled, tabStop, offer, choose }),
1067
1053
  [system, setSystem, disabled, tabStop, offer]
1068
1054
  );
@@ -1091,7 +1077,7 @@ var SystemToggle = /* @__PURE__ */ (0, import_react14.forwardRef)(
1091
1077
  ) });
1092
1078
  }
1093
1079
  );
1094
- var SystemOption = /* @__PURE__ */ (0, import_react14.forwardRef)(
1080
+ var SystemOption = /* @__PURE__ */ (0, import_react.forwardRef)(
1095
1081
  function SystemOption2({
1096
1082
  value,
1097
1083
  asChild = false,
@@ -1114,10 +1100,10 @@ var SystemOption = /* @__PURE__ */ (0, import_react14.forwardRef)(
1114
1100
  "<Recipe.SystemOption> must be rendered inside <Recipe.SystemToggle>."
1115
1101
  );
1116
1102
  const { labels } = useRecipeData("Recipe.SystemOption");
1117
- const id = (0, import_react14.useId)();
1103
+ const id = (0, import_react22.useId)();
1118
1104
  const disabled = groupDisabled || ownDisabled;
1119
1105
  const checked = value === system;
1120
- (0, import_react14.useEffect)(
1106
+ (0, import_react22.useEffect)(
1121
1107
  () => disabled ? void 0 : offer(id, value),
1122
1108
  [disabled, offer, id, value]
1123
1109
  );
@@ -1132,6 +1118,7 @@ var SystemOption = /* @__PURE__ */ (0, import_react14.forwardRef)(
1132
1118
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1133
1119
  Comp,
1134
1120
  {
1121
+ autoComplete: "off",
1135
1122
  ...props,
1136
1123
  type: type ?? "button",
1137
1124
  role: "radio",