@flamingo-stack/openframe-frontend-core 0.0.742 → 0.0.744

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 (94) hide show
  1. package/dist/build/export-map.json +1 -1
  2. package/dist/{chunk-54EA4RLX.cjs → chunk-2JRCJV3M.cjs} +3 -3
  3. package/dist/{chunk-ZIFYSLIM.cjs → chunk-2UBR53AN.cjs} +4 -4
  4. package/dist/{chunk-Z2DQYKZA.cjs → chunk-35IQLGV3.cjs} +69 -69
  5. package/dist/{chunk-WIHHA54J.cjs → chunk-4A6OF4HI.cjs} +44 -44
  6. package/dist/{chunk-OBODY7F3.cjs → chunk-4N7KGOBS.cjs} +3 -3
  7. package/dist/{chunk-HIRVNBX5.cjs → chunk-65LJNDR2.cjs} +16 -16
  8. package/dist/{chunk-32Q7K5OP.cjs → chunk-7YDF3QBN.cjs} +116 -115
  9. package/dist/{chunk-DNFSMVXJ.cjs → chunk-AT6ZI4CO.cjs} +12 -12
  10. package/dist/{chunk-UWHCRIQQ.cjs → chunk-FTYQS6VI.cjs} +13 -13
  11. package/dist/{chunk-YQGDXH3M.cjs → chunk-GAGNKWOC.cjs} +2 -2
  12. package/dist/{chunk-L34TKFTS.cjs → chunk-H62FNZGG.cjs} +16 -16
  13. package/dist/{chunk-T433HSL6.cjs → chunk-HHL23RFC.cjs} +33 -33
  14. package/dist/{chunk-4PKORVOX.cjs → chunk-HJ4KGA2M.cjs} +441 -406
  15. package/dist/{chunk-4LDU32OB.cjs → chunk-HMSUYWSB.cjs} +4 -4
  16. package/dist/{chunk-UZLRSFGG.cjs → chunk-HTQJYN47.cjs} +30 -30
  17. package/dist/{chunk-XAA5EZ5T.cjs → chunk-JGZEHBBD.cjs} +6 -6
  18. package/dist/{chunk-5MH6BRPY.cjs → chunk-LJ46XYYP.cjs} +24 -12
  19. package/dist/{chunk-ISJXKKYW.cjs → chunk-NURAL5HD.cjs} +11 -11
  20. package/dist/{chunk-PS6T65KA.cjs → chunk-OC2LBQQJ.cjs} +90 -66
  21. package/dist/{chunk-HDBY2URA.cjs → chunk-PYWHDPVC.cjs} +31 -31
  22. package/dist/{chunk-R6SCSV2I.cjs → chunk-QSO56JZ2.cjs} +81 -81
  23. package/dist/{chunk-3LW43GIJ.cjs → chunk-QZYF2YQ3.cjs} +4 -4
  24. package/dist/{chunk-BI3XBCQX.cjs → chunk-YIOWV7LS.cjs} +2 -2
  25. package/dist/{chunk-H3UA7UY5.cjs → chunk-YJRMCFX3.cjs} +2 -2
  26. package/dist/{cloud-tenants-6ICAL3HO.cjs → cloud-tenants-JPXAXSPE.cjs} +7 -7
  27. package/dist/components/case-studies/index.cjs +19 -19
  28. package/dist/components/chat/entity-cards/onboarding-guide-card.d.ts.map +1 -1
  29. package/dist/components/chat/entity-cards/onboarding-guide-card.js +22 -12
  30. package/dist/components/chat/index.cjs +5 -5
  31. package/dist/components/chat/tool-execution-display.d.ts.map +1 -1
  32. package/dist/components/chat/tool-execution-display.js +5 -4
  33. package/dist/components/chat/utils/history-merge.d.ts.map +1 -1
  34. package/dist/components/chat/utils/history-merge.js +25 -1
  35. package/dist/components/contact/index.cjs +8 -8
  36. package/dist/components/docs/index.cjs +11 -11
  37. package/dist/components/embeds/index.cjs +8 -8
  38. package/dist/components/faq/index.cjs +9 -9
  39. package/dist/components/features/index.cjs +7 -7
  40. package/dist/components/features/scripts/schedule-form/schedule-interval-input.d.ts +1 -1
  41. package/dist/components/features/scripts/schedule-form/schedule-interval-input.d.ts.map +1 -1
  42. package/dist/components/features/scripts/schedule-form/schedule-interval-input.js +15 -13
  43. package/dist/components/features/scripts/schedule-form/schedule-offline-fields.d.ts.map +1 -1
  44. package/dist/components/features/scripts/schedule-form/schedule-offline-fields.js +9 -7
  45. package/dist/components/help-center-pages/index.cjs +101 -101
  46. package/dist/components/index.cjs +59 -57
  47. package/dist/components/meeting-scheduler/booking-form.d.ts +11 -1
  48. package/dist/components/meeting-scheduler/booking-form.d.ts.map +1 -1
  49. package/dist/components/meeting-scheduler/booking-form.js +15 -3
  50. package/dist/components/meeting-scheduler/index.cjs +408 -114
  51. package/dist/components/meeting-scheduler/index.d.ts +16 -1
  52. package/dist/components/meeting-scheduler/index.d.ts.map +1 -1
  53. package/dist/components/meeting-scheduler/index.js +157 -5
  54. package/dist/components/meeting-scheduler/modal-header.d.ts +27 -0
  55. package/dist/components/meeting-scheduler/modal-header.d.ts.map +1 -0
  56. package/dist/components/meeting-scheduler/modal-header.js +104 -0
  57. package/dist/components/meeting-scheduler/slot-picker.d.ts +13 -2
  58. package/dist/components/meeting-scheduler/slot-picker.d.ts.map +1 -1
  59. package/dist/components/meeting-scheduler/slot-picker.js +112 -73
  60. package/dist/components/navigation/index.cjs +10 -10
  61. package/dist/components/onboarding-guides/index.cjs +10 -10
  62. package/dist/components/product-demo/index.cjs +7 -7
  63. package/dist/components/related-content/index.cjs +5 -5
  64. package/dist/components/shared/product-release/product-release-card.d.ts.map +1 -1
  65. package/dist/components/shared/product-release/product-release-card.js +2 -0
  66. package/dist/components/tickets/index.cjs +13 -13
  67. package/dist/components/ui/index.cjs +9 -7
  68. package/dist/components/ui/radio-group.d.ts +6 -0
  69. package/dist/components/ui/radio-group.d.ts.map +1 -1
  70. package/dist/components/ui/radio-group.js +65 -58
  71. package/dist/components/ui/tab-navigation.d.ts +19 -1
  72. package/dist/components/ui/tab-navigation.d.ts.map +1 -1
  73. package/dist/components/ui/tab-navigation.js +32 -8
  74. package/dist/{devices-OFP2IBKN.cjs → devices-ONC4JOVH.cjs} +8 -8
  75. package/dist/{embeddable-chat-4C22MGNC.cjs → embeddable-chat-7ZEGCDVF.cjs} +71 -71
  76. package/dist/index.cjs +13 -11
  77. package/dist/{knowledge-KKBCSNUA.cjs → knowledge-G7XU5YST.cjs} +8 -8
  78. package/dist/{logs-X2GRNO57.cjs → logs-5B7NY4G6.cjs} +15 -15
  79. package/dist/{policies-35BXKB6P.cjs → policies-OYR3SLJN.cjs} +11 -11
  80. package/dist/{scripts-KQYVQOLU.cjs → scripts-6CWHTY6U.cjs} +12 -12
  81. package/dist/{software-update-ED33WSKT.cjs → software-update-AJPIY4SQ.cjs} +10 -10
  82. package/package.json +1 -1
  83. package/src/components/chat/entity-cards/onboarding-guide-card.tsx +8 -1
  84. package/src/components/chat/tool-execution-display.tsx +21 -10
  85. package/src/components/chat/utils/history-merge.ts +46 -1
  86. package/src/components/features/scripts/schedule-form/schedule-interval-input.tsx +20 -13
  87. package/src/components/features/scripts/schedule-form/schedule-offline-fields.tsx +9 -5
  88. package/src/components/meeting-scheduler/booking-form.tsx +29 -3
  89. package/src/components/meeting-scheduler/index.tsx +224 -3
  90. package/src/components/meeting-scheduler/modal-header.tsx +145 -0
  91. package/src/components/meeting-scheduler/slot-picker.tsx +72 -8
  92. package/src/components/shared/product-release/product-release-card.tsx +2 -0
  93. package/src/components/ui/radio-group.tsx +51 -36
  94. package/src/components/ui/tab-navigation.tsx +54 -5
@@ -47,6 +47,12 @@ interface RadioGroupBlockOption {
47
47
  disabled?: boolean;
48
48
  /** Optional trailing slot rendered at the end of the row (e.g. a discount tag) */
49
49
  trailing?: ReactNode;
50
+ /**
51
+ * Optional content under the option's row, inside the option and across its
52
+ * full width (e.g. the fields the option needs on a narrow screen). Rendered
53
+ * as given, with no wrapper, so a footer hidden at some widths leaves no gap.
54
+ */
55
+ footer?: ReactNode;
50
56
  }
51
57
 
52
58
  interface RadioGroupBlockProps extends Omit<ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>, 'children'> {
@@ -99,42 +105,9 @@ const RadioGroupBlock = forwardRef<ComponentRef<typeof RadioGroupPrimitive.Root>
99
105
  const itemId = `${props.name ?? 'radio-block'}-${option.value}`;
100
106
  const isDisabled = disabled || option.disabled;
101
107
  const isLast = index === options.length - 1;
102
- return (
103
- <label
104
- key={option.value}
105
- htmlFor={itemId}
106
- aria-disabled={isDisabled || undefined}
107
- className={cn(
108
- 'flex w-full items-center gap-3',
109
- isHorizontal && 'min-w-0 flex-1',
110
- 'transition-colors duration-200',
111
- isDisabled ? 'cursor-not-allowed' : 'cursor-pointer',
112
- // Hover / active move the BACKGROUND only, like Input and
113
- // CheckboxBlock — the border stays put so an unselected row
114
- // never flashes "selected" under the cursor.
115
- !isDisabled && 'hover:bg-ods-bg-hover active:bg-ods-bg-active',
116
- isGrouped
117
- ? cn(
118
- 'bg-ods-card px-[var(--spacing-system-sf)] py-[var(--spacing-system-xs,8px)]',
119
- !isLast && (isHorizontal ? 'border-r' : 'border-b'),
120
- error ? 'border-ods-error' : 'border-ods-border',
121
- )
122
- : cn(
123
- 'rounded-[6px] border bg-ods-card p-[var(--spacing-system-sf)]',
124
- !isDisabled &&
125
- 'has-[[data-state=checked]]:bg-[var(--ods-system-greys-soft-grey)]/30 has-[[data-state=checked]]:border-ods-accent/40',
126
- error ? 'border-ods-error' : 'border-ods-border',
127
- ),
128
- // Disabled matches a disabled field: flat `ods-bg` fill and
129
- // greyed text, not a fade of the whole row. The greying lives
130
- // on the TEXT COLUMN below, not here: as a descendant rule on
131
- // the row it also repainted whatever `trailing` renders — a
132
- // `Tag`'s label is a span too, and it went invisible against
133
- // its own fill.
134
- isDisabled && 'bg-ods-bg',
135
- itemClassName,
136
- )}
137
- >
108
+ const hasFooter = option.footer != null;
109
+ const row = (
110
+ <>
138
111
  <RadioGroupPrimitive.Item
139
112
  id={itemId}
140
113
  value={option.value}
@@ -186,6 +159,48 @@ const RadioGroupBlock = forwardRef<ComponentRef<typeof RadioGroupPrimitive.Root>
186
159
  )}
187
160
  </div>
188
161
  {option.trailing && <div className="ml-auto flex shrink-0 items-center">{option.trailing}</div>}
162
+ </>
163
+ );
164
+ return (
165
+ <label
166
+ key={option.value}
167
+ htmlFor={itemId}
168
+ aria-disabled={isDisabled || undefined}
169
+ className={cn(
170
+ 'flex w-full items-center gap-3',
171
+ isHorizontal && 'min-w-0 flex-1',
172
+ 'transition-colors duration-200',
173
+ isDisabled ? 'cursor-not-allowed' : 'cursor-pointer',
174
+ // Hover / active move the BACKGROUND only, like Input and
175
+ // CheckboxBlock — the border stays put so an unselected row
176
+ // never flashes "selected" under the cursor.
177
+ !isDisabled && 'hover:bg-ods-bg-hover active:bg-ods-bg-active',
178
+ isGrouped
179
+ ? cn(
180
+ 'bg-ods-card px-[var(--spacing-system-sf)] py-[var(--spacing-system-xs,8px)]',
181
+ !isLast && (isHorizontal ? 'border-r' : 'border-b'),
182
+ error ? 'border-ods-error' : 'border-ods-border',
183
+ )
184
+ : cn(
185
+ 'rounded-[6px] border bg-ods-card p-[var(--spacing-system-sf)]',
186
+ !isDisabled &&
187
+ 'has-[[data-state=checked]]:bg-[var(--ods-system-greys-soft-grey)]/30 has-[[data-state=checked]]:border-ods-accent/40',
188
+ error ? 'border-ods-error' : 'border-ods-border',
189
+ ),
190
+ // Disabled matches a disabled field: flat `ods-bg` fill and
191
+ // greyed text, not a fade of the whole row. The greying lives
192
+ // on the TEXT COLUMN below, not here: as a descendant rule on
193
+ // the row it also repainted whatever `trailing` renders — a
194
+ // `Tag`'s label is a span too, and it went invisible against
195
+ // its own fill.
196
+ isDisabled && 'bg-ods-bg',
197
+ // With a footer the option stacks: its row, then the footer.
198
+ hasFooter && 'flex-col items-stretch gap-[var(--spacing-system-sf)]',
199
+ itemClassName,
200
+ )}
201
+ >
202
+ {hasFooter ? <div className="flex w-full items-center gap-[var(--spacing-system-sf)]">{row}</div> : row}
203
+ {option.footer}
189
204
  </label>
190
205
  );
191
206
  })}
@@ -2,6 +2,8 @@
2
2
 
3
3
  import type React from 'react';
4
4
  import {
5
+ createContext,
6
+ useContext,
5
7
  useState,
6
8
  useEffect,
7
9
  useLayoutEffect,
@@ -71,6 +73,31 @@ export interface TabItem {
71
73
  accent?: string;
72
74
  }
73
75
 
76
+ /**
77
+ * How a strip marks its tabs.
78
+ *
79
+ * - `default`: the tabs touch, and the open tab is marked by the underline alone.
80
+ * - `admin`: the product admin's tab bar. A gap between tabs, and the open and the
81
+ * hovered tab carry an accent fade above the underline.
82
+ */
83
+ export type TabNavigationVariant = 'default' | 'admin';
84
+
85
+ const TabNavigationVariantContext = createContext<TabNavigationVariant>('default');
86
+
87
+ /**
88
+ * Sets the variant of every `TabNavigation` below it that does not pass its own
89
+ * `variant`. An app whose tab bars all share one look mounts it once in its layout.
90
+ */
91
+ export function TabNavigationVariantProvider({
92
+ variant,
93
+ children,
94
+ }: {
95
+ variant: TabNavigationVariant;
96
+ children: React.ReactNode;
97
+ }) {
98
+ return <TabNavigationVariantContext.Provider value={variant}>{children}</TabNavigationVariantContext.Provider>;
99
+ }
100
+
74
101
  export interface TabNavigationUrlSyncOptions {
75
102
  paramName?: string; // Default: 'tab'
76
103
  replaceState?: boolean; // Default: true (use replace instead of push)
@@ -99,6 +126,8 @@ interface TabNavigationProps {
99
126
  tabAlign?: 'center' | 'start';
100
127
  /** How long the underline takes to reach a new `TabItem.progress` value. Default 200. */
101
128
  progressTransitionMs?: number;
129
+ /** How the strip marks its tabs. Omitted: the nearest `TabNavigationVariantProvider`'s, else `default`. */
130
+ variant?: TabNavigationVariant;
102
131
 
103
132
  // URL sync mode
104
133
  urlSync?: boolean | TabNavigationUrlSyncOptions;
@@ -145,6 +174,7 @@ interface TabBarProps {
145
174
  stretchTabs: boolean;
146
175
  tabAlign: 'center' | 'start';
147
176
  progressTransitionMs: number;
177
+ variant: TabNavigationVariant;
148
178
  }
149
179
 
150
180
  /**
@@ -170,7 +200,9 @@ const TabBar = memo(function TabBarImpl({
170
200
  stretchTabs,
171
201
  tabAlign,
172
202
  progressTransitionMs,
203
+ variant,
173
204
  }: TabBarProps) {
205
+ const isAdmin = variant === 'admin';
174
206
  const scrollRef = useRef<HTMLDivElement>(null);
175
207
  const activeTabRef = useRef<HTMLButtonElement>(null);
176
208
  const isFirstActiveScrollRef = useRef(true);
@@ -396,7 +428,10 @@ const TabBar = memo(function TabBarImpl({
396
428
  `relative` makes this the offsetParent the underline is measured against. */}
397
429
  <div
398
430
  ref={scrollRef}
399
- className="scrollbar-hide relative flex h-full items-center justify-start overflow-x-auto overflow-y-hidden"
431
+ className={cn(
432
+ 'scrollbar-hide relative flex h-full items-center justify-start overflow-x-auto overflow-y-hidden',
433
+ isAdmin && 'gap-[var(--spacing-system-xxs)]',
434
+ )}
400
435
  >
401
436
  {tabs.map(tab => {
402
437
  const isActive = activeTab === tab.id;
@@ -417,11 +452,19 @@ const TabBar = memo(function TabBarImpl({
417
452
  // `outline-none` above drops the browser's own focus ring, so
418
453
  // put one back or the strip is un-navigable by keyboard — you
419
454
  // can tab through it with nothing to show where you are. Inset,
420
- // so it stays within the button: the tabs touch.
455
+ // so it stays within the button and never overlaps a neighbour.
421
456
  'focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ods-focus',
422
457
  stretchTabs && 'flex-1',
423
458
  tab.avatar && 'gap-[var(--spacing-system-xsf)]',
424
- // The open tab is marked by the underline alone: no surface of its own.
459
+ // `default`: the open tab is marked by the underline alone, no surface
460
+ // of its own. `admin`: the open and the hovered tab carry an accent
461
+ // fade. ODS colour vars hold hex values, so Tailwind alpha modifiers
462
+ // (to-ods-accent/10) silently produce no CSS; color-mix() accepts
463
+ // hex, so the 10%-alpha stop is derived from the token.
464
+ isAdmin &&
465
+ (isActive
466
+ ? 'bg-gradient-to-b from-transparent to-[color-mix(in_srgb,var(--color-accent-primary)_10%,transparent)]'
467
+ : 'hover:bg-gradient-to-b hover:from-transparent hover:to-[color-mix(in_srgb,var(--color-accent-primary)_10%,transparent)]'),
425
468
  )}
426
469
  >
427
470
  {tab.avatar ? (
@@ -436,7 +479,7 @@ const TabBar = memo(function TabBarImpl({
436
479
  'h-4 w-4 transition-colors content-md:h-6 content-md:w-6',
437
480
  isActive
438
481
  ? !tab.accent && 'text-ods-accent'
439
- : 'text-ods-text-secondary group-hover:text-ods-text-primary',
482
+ : cn('text-ods-text-secondary', !isAdmin && 'group-hover:text-ods-text-primary'),
440
483
  )}
441
484
  style={isActive && tab.accent ? { color: tab.accent } : undefined}
442
485
  />
@@ -453,7 +496,9 @@ const TabBar = memo(function TabBarImpl({
453
496
  <span
454
497
  className={cn(
455
498
  'whitespace-nowrap transition-colors text-h4',
456
- isActive ? 'text-ods-text-primary' : 'text-ods-text-secondary group-hover:text-ods-text-primary',
499
+ isActive
500
+ ? 'text-ods-text-primary'
501
+ : cn('text-ods-text-secondary', !isAdmin && 'group-hover:text-ods-text-primary'),
457
502
  )}
458
503
  >
459
504
  {tab.label}
@@ -542,10 +587,13 @@ export function TabNavigation({
542
587
  stretchTabs = false,
543
588
  tabAlign = 'center',
544
589
  progressTransitionMs = 200,
590
+ variant: variantProp,
545
591
  urlSync = false,
546
592
  defaultTab,
547
593
  children,
548
594
  }: TabNavigationProps) {
595
+ const providedVariant = useContext(TabNavigationVariantContext);
596
+ const variant = variantProp ?? providedVariant;
549
597
  const router = useRouter();
550
598
  const pathname = usePathname();
551
599
  const searchParams = useSearchParams();
@@ -717,6 +765,7 @@ export function TabNavigation({
717
765
  stretchTabs={stretchTabs}
718
766
  tabAlign={tabAlign}
719
767
  progressTransitionMs={progressTransitionMs}
768
+ variant={variant}
720
769
  />
721
770
 
722
771
  {/* Render children with active tab if provided */}