@flamingo-stack/openframe-frontend-core 0.0.325 → 0.0.326-snapshot.20260626135722

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 (98) hide show
  1. package/dist/{chunk-U74OC6PM.cjs → chunk-3AXH7BDM.cjs} +26 -26
  2. package/dist/{chunk-U74OC6PM.cjs.map → chunk-3AXH7BDM.cjs.map} +1 -1
  3. package/dist/{chunk-VSUBZBG7.js → chunk-3UVR2JIB.js} +2 -2
  4. package/dist/{chunk-GYWMN4AK.js → chunk-46GJFN4V.js} +4 -4
  5. package/dist/{chunk-5GXMQPG7.cjs → chunk-4ZHA24HS.cjs} +37 -37
  6. package/dist/{chunk-5GXMQPG7.cjs.map → chunk-4ZHA24HS.cjs.map} +1 -1
  7. package/dist/{chunk-VUE2FSVD.js → chunk-5WDRXBM3.js} +2 -2
  8. package/dist/{chunk-HS2YU2NF.js → chunk-62O2VTR2.js} +2 -2
  9. package/dist/{chunk-XTV2EZHO.cjs → chunk-7FY2OM5W.cjs} +31 -31
  10. package/dist/{chunk-XTV2EZHO.cjs.map → chunk-7FY2OM5W.cjs.map} +1 -1
  11. package/dist/{chunk-5COLEZW6.cjs → chunk-BWWUHOE3.cjs} +62 -62
  12. package/dist/{chunk-5COLEZW6.cjs.map → chunk-BWWUHOE3.cjs.map} +1 -1
  13. package/dist/{chunk-5DXWGCTW.cjs → chunk-CRHOAKG5.cjs} +56 -56
  14. package/dist/{chunk-5DXWGCTW.cjs.map → chunk-CRHOAKG5.cjs.map} +1 -1
  15. package/dist/{chunk-P3IIAUCL.js → chunk-CWXO6VCD.js} +4253 -4172
  16. package/dist/chunk-CWXO6VCD.js.map +1 -0
  17. package/dist/{chunk-AYBSXRRC.cjs → chunk-GABTFTDE.cjs} +879 -798
  18. package/dist/chunk-GABTFTDE.cjs.map +1 -0
  19. package/dist/{chunk-4USOR7KK.cjs → chunk-HQ6BILVP.cjs} +25 -25
  20. package/dist/{chunk-4USOR7KK.cjs.map → chunk-HQ6BILVP.cjs.map} +1 -1
  21. package/dist/{chunk-WAYJ7VBD.cjs → chunk-I4OFXYJR.cjs} +12 -12
  22. package/dist/{chunk-WAYJ7VBD.cjs.map → chunk-I4OFXYJR.cjs.map} +1 -1
  23. package/dist/{chunk-XFRQUV5M.js → chunk-IDMM6DVL.js} +2 -2
  24. package/dist/{chunk-MIVP6B6P.js → chunk-J4HHXHMC.js} +4 -4
  25. package/dist/{chunk-6GSM6K5W.js → chunk-LESTX5DM.js} +2 -2
  26. package/dist/{chunk-EQSGC5WT.js → chunk-MJUYWNQB.js} +2 -2
  27. package/dist/{chunk-5HRCKYFX.cjs → chunk-MWAEG3XK.cjs} +9 -9
  28. package/dist/{chunk-5HRCKYFX.cjs.map → chunk-MWAEG3XK.cjs.map} +1 -1
  29. package/dist/{chunk-XQBK7IVF.cjs → chunk-NROSNR5J.cjs} +15 -15
  30. package/dist/{chunk-XQBK7IVF.cjs.map → chunk-NROSNR5J.cjs.map} +1 -1
  31. package/dist/{chunk-QLSUOQMU.cjs → chunk-PGFQ6WQR.cjs} +7 -7
  32. package/dist/{chunk-QLSUOQMU.cjs.map → chunk-PGFQ6WQR.cjs.map} +1 -1
  33. package/dist/{chunk-UJIDDEXD.js → chunk-QF4XERYJ.js} +2 -2
  34. package/dist/{chunk-5CWJGCOL.js → chunk-U7GDY7Y3.js} +4 -4
  35. package/dist/{chunk-5YGKK636.js → chunk-UHHJIHUZ.js} +4 -4
  36. package/dist/{chunk-K2PS5MJH.cjs → chunk-XDNQJQGP.cjs} +5 -5
  37. package/dist/{chunk-K2PS5MJH.cjs.map → chunk-XDNQJQGP.cjs.map} +1 -1
  38. package/dist/components/case-studies/index.cjs +8 -8
  39. package/dist/components/case-studies/index.js +2 -2
  40. package/dist/components/chat/index.cjs +2 -2
  41. package/dist/components/chat/index.js +1 -1
  42. package/dist/components/contact/index.cjs +3 -3
  43. package/dist/components/contact/index.js +2 -2
  44. package/dist/components/docs/index.cjs +5 -5
  45. package/dist/components/docs/index.js +4 -4
  46. package/dist/components/embeds/index.cjs +3 -3
  47. package/dist/components/embeds/index.js +2 -2
  48. package/dist/components/faq/index.cjs +3 -3
  49. package/dist/components/faq/index.js +2 -2
  50. package/dist/components/features/index.cjs +2 -2
  51. package/dist/components/features/index.js +1 -1
  52. package/dist/components/help-center-pages/index.cjs +20 -20
  53. package/dist/components/help-center-pages/index.js +11 -11
  54. package/dist/components/index.cjs +122 -120
  55. package/dist/components/index.cjs.map +1 -1
  56. package/dist/components/index.js +11 -9
  57. package/dist/components/index.js.map +1 -1
  58. package/dist/components/navigation/index.cjs +2 -2
  59. package/dist/components/navigation/index.js +1 -1
  60. package/dist/components/onboarding-guides/index.cjs +5 -5
  61. package/dist/components/onboarding-guides/index.js +4 -4
  62. package/dist/components/related-content/index.cjs +3 -3
  63. package/dist/components/related-content/index.js +2 -2
  64. package/dist/components/tickets/index.cjs +5 -5
  65. package/dist/components/tickets/index.js +4 -4
  66. package/dist/components/ui/index.cjs +4 -2
  67. package/dist/components/ui/index.cjs.map +1 -1
  68. package/dist/components/ui/index.d.ts +1 -0
  69. package/dist/components/ui/index.d.ts.map +1 -1
  70. package/dist/components/ui/index.js +3 -1
  71. package/dist/components/ui/info-section.d.ts +63 -0
  72. package/dist/components/ui/info-section.d.ts.map +1 -0
  73. package/dist/components/ui/page-actions.d.ts +2 -0
  74. package/dist/components/ui/page-actions.d.ts.map +1 -1
  75. package/dist/components/ui/ticket-attachments-list.d.ts +2 -0
  76. package/dist/components/ui/ticket-attachments-list.d.ts.map +1 -1
  77. package/dist/index.cjs +4 -2
  78. package/dist/index.cjs.map +1 -1
  79. package/dist/index.js +3 -1
  80. package/package.json +1 -1
  81. package/src/components/ui/index.ts +1 -0
  82. package/src/components/ui/info-section.tsx +151 -0
  83. package/src/components/ui/page-actions.tsx +16 -0
  84. package/src/components/ui/ticket-attachments-list.tsx +14 -2
  85. package/src/stories/InfoSection.stories.tsx +110 -0
  86. package/dist/chunk-AYBSXRRC.cjs.map +0 -1
  87. package/dist/chunk-P3IIAUCL.js.map +0 -1
  88. /package/dist/{chunk-VSUBZBG7.js.map → chunk-3UVR2JIB.js.map} +0 -0
  89. /package/dist/{chunk-GYWMN4AK.js.map → chunk-46GJFN4V.js.map} +0 -0
  90. /package/dist/{chunk-VUE2FSVD.js.map → chunk-5WDRXBM3.js.map} +0 -0
  91. /package/dist/{chunk-HS2YU2NF.js.map → chunk-62O2VTR2.js.map} +0 -0
  92. /package/dist/{chunk-XFRQUV5M.js.map → chunk-IDMM6DVL.js.map} +0 -0
  93. /package/dist/{chunk-MIVP6B6P.js.map → chunk-J4HHXHMC.js.map} +0 -0
  94. /package/dist/{chunk-6GSM6K5W.js.map → chunk-LESTX5DM.js.map} +0 -0
  95. /package/dist/{chunk-EQSGC5WT.js.map → chunk-MJUYWNQB.js.map} +0 -0
  96. /package/dist/{chunk-UJIDDEXD.js.map → chunk-QF4XERYJ.js.map} +0 -0
  97. /package/dist/{chunk-5CWJGCOL.js.map → chunk-U7GDY7Y3.js.map} +0 -0
  98. /package/dist/{chunk-5YGKK636.js.map → chunk-UHHJIHUZ.js.map} +0 -0
@@ -6,6 +6,7 @@ import { Chevron02DownIcon } from '../icons-v2-generated'
6
6
  import { ActionsMenuDropdown, type ActionsMenuGroup, type ActionsMenuItem } from './actions-menu'
7
7
  import type { ButtonProps, SplitButtonIconAction } from './button'
8
8
  import { Button, SplitButton } from './button'
9
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './tooltip'
9
10
 
10
11
  export type PageActionButton = {
11
12
  /** Button label. Omit to render an icon-only button. */
@@ -50,6 +51,8 @@ export type PageActionButton = {
50
51
  * Mutually exclusive with `iconAction` and `href`/`onClick`.
51
52
  */
52
53
  submenu?: ActionsMenuItem[]
54
+ /** When set, the rendered desktop button is wrapped in a hover tooltip. */
55
+ tooltip?: React.ReactNode
53
56
  }
54
57
 
55
58
  function actionKey(action: PageActionButton, idx: number) {
@@ -97,6 +100,19 @@ interface RenderOptions {
97
100
  }
98
101
 
99
102
  function renderActionButton(action: PageActionButton, opts: RenderOptions = {}): React.ReactNode {
103
+ const button = renderRawActionButton(action, opts)
104
+ if (!action.tooltip) return button
105
+ return (
106
+ <TooltipProvider delayDuration={0}>
107
+ <Tooltip>
108
+ <TooltipTrigger asChild>{button}</TooltipTrigger>
109
+ <TooltipContent>{action.tooltip}</TooltipContent>
110
+ </Tooltip>
111
+ </TooltipProvider>
112
+ )
113
+ }
114
+
115
+ function renderRawActionButton(action: PageActionButton, opts: RenderOptions = {}): React.ReactNode {
100
116
  // Two-target SplitButton — primary action + secondary icon action.
101
117
  if (action.iconAction) {
102
118
  return (
@@ -1,7 +1,7 @@
1
1
  "use client"
2
2
 
3
3
  import * as React from "react"
4
- import { Download, FileIcon } from "lucide-react"
4
+ import { Download02Icon, FileIcon, TrashIcon } from "../icons-v2-generated"
5
5
  import { cn } from "../../utils/cn"
6
6
  import { SquareAvatar } from "./square-avatar"
7
7
 
@@ -11,6 +11,8 @@ export interface TicketAttachment {
11
11
  fileSize: string
12
12
  thumbnailSrc?: string
13
13
  onDownload?: () => void
14
+ /** When set, a trash button is rendered before the download button. */
15
+ onDelete?: () => void
14
16
  }
15
17
 
16
18
  export interface TicketAttachmentsListProps {
@@ -65,6 +67,16 @@ export function TicketAttachmentsList({ attachments, className, size = 'default'
65
67
  <p className="text-h6 text-ods-text-secondary">{attachment.fileSize}</p>
66
68
  )}
67
69
  </div>
70
+ {attachment.onDelete && (
71
+ <button
72
+ type="button"
73
+ onClick={attachment.onDelete}
74
+ className="shrink-0 text-ods-text-secondary hover:text-ods-error transition-colors"
75
+ aria-label={`Delete ${attachment.fileName}`}
76
+ >
77
+ <TrashIcon className={compact ? "size-4" : "size-5"} />
78
+ </button>
79
+ )}
68
80
  {attachment.onDownload && (
69
81
  <button
70
82
  type="button"
@@ -72,7 +84,7 @@ export function TicketAttachmentsList({ attachments, className, size = 'default'
72
84
  className="shrink-0 text-ods-text-secondary hover:text-ods-text-primary transition-colors"
73
85
  aria-label={`Download ${attachment.fileName}`}
74
86
  >
75
- <Download className={compact ? "size-4" : "size-6"} />
87
+ <Download02Icon className={compact ? "size-4" : "size-6"} />
76
88
  </button>
77
89
  )}
78
90
  </div>
@@ -0,0 +1,110 @@
1
+ import type { Meta, StoryObj } from '@storybook/nextjs-vite';
2
+ import { useState } from 'react';
3
+ import { Badge } from '../components/ui/badge';
4
+ import { InfoSection, type InfoSectionRow } from '../components/ui/info-section';
5
+
6
+ const meta = {
7
+ title: 'Tickets/InfoSection',
8
+ component: InfoSection,
9
+ parameters: {
10
+ layout: 'padded',
11
+ docs: {
12
+ description: {
13
+ component:
14
+ 'A labelled info card: a vertical stack of `label —— value` rows. Each value is configurable — plain text (with an optional trailing image), an inline assignee picker (autocomplete with search), an inline status dropdown, or an arbitrary node.',
15
+ },
16
+ },
17
+ },
18
+ tags: ['autodocs'],
19
+ decorators: [
20
+ Story => (
21
+ <div style={{ width: '360px' }}>
22
+ <Story />
23
+ </div>
24
+ ),
25
+ ],
26
+ } satisfies Meta<typeof InfoSection>;
27
+
28
+ export default meta;
29
+ type Story = StoryObj<typeof meta>;
30
+
31
+ const assigneeOptions = [
32
+ { value: 'u1', label: 'Roman Smith' },
33
+ { value: 'u2', label: 'John Doe' },
34
+ { value: 'u3', label: 'Marcus Chen' },
35
+ ];
36
+
37
+ const statusOptions = [
38
+ { id: 's1', name: 'AI Assistance', color: '#3a3a3a' },
39
+ { id: 's2', name: 'Tech Required', color: '#f59e0b' },
40
+ { id: 's3', name: 'Resolved', color: '#5ea62e' },
41
+ ];
42
+
43
+ /**
44
+ * The "Ticket Details" card from the design: text, text-with-avatar, an inline
45
+ * assignee picker, and an inline status dropdown.
46
+ */
47
+ export const TicketDetails: Story = {
48
+ args: {
49
+ title: 'Ticket Details',
50
+ rows: [
51
+ { id: 'customer', label: 'Customer', value: { text: 'Acme Corp', imageFallback: 'Acme Corp' } },
52
+ { id: 'device', label: 'Device', value: { text: 'TB-156-SEA', onClick: () => alert('Navigate to device') } },
53
+ {
54
+ id: 'assigned',
55
+ label: 'Assigned',
56
+ value: { type: 'assignee', options: assigneeOptions, onAssign: id => alert(`Assign: ${id}`) },
57
+ },
58
+ { id: 'created', label: 'Created', value: { text: '07/12/25, 10:03 AM' } },
59
+ { id: 'created-by', label: 'Created by', value: { text: 'Fae', imageFallback: 'Fae' } },
60
+ {
61
+ id: 'status',
62
+ label: 'Status',
63
+ value: { type: 'status', status: 'AI_ASSISTANCE', options: statusOptions, onSelect: id => alert(`Status: ${id}`) },
64
+ },
65
+ ],
66
+ },
67
+ };
68
+
69
+ /**
70
+ * Live state: assigning a user and changing status update the rows in place.
71
+ */
72
+ export const Interactive: Story = {
73
+ args: { title: 'Ticket Details', rows: [] },
74
+ render: function InteractiveStory() {
75
+ const [assignee, setAssignee] = useState<string | null>(null);
76
+ const [statusId, setStatusId] = useState('s1');
77
+
78
+ const current = assigneeOptions.find(o => o.value === assignee);
79
+ const status = statusOptions.find(o => o.id === statusId);
80
+
81
+ const rows: InfoSectionRow[] = [
82
+ { id: 'customer', label: 'Customer', value: { text: 'Acme Corp', imageFallback: 'Acme Corp' } },
83
+ {
84
+ id: 'assigned',
85
+ label: 'Assigned',
86
+ value: {
87
+ type: 'assignee',
88
+ currentAssignee: current ? { id: current.value, name: current.label } : undefined,
89
+ options: assigneeOptions,
90
+ onAssign: setAssignee,
91
+ },
92
+ },
93
+ {
94
+ id: 'status',
95
+ label: 'Status',
96
+ value: {
97
+ type: 'status',
98
+ status: 'AI_ASSISTANCE',
99
+ label: status?.name,
100
+ color: status?.color,
101
+ options: statusOptions,
102
+ onSelect: setStatusId,
103
+ },
104
+ },
105
+ { id: 'priority', label: 'Priority', value: { type: 'custom', content: <Badge>High</Badge> } },
106
+ ];
107
+
108
+ return <InfoSection title="Ticket Details" rows={rows} />;
109
+ },
110
+ };