arxatec-ui 0.1.71 → 0.1.72

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 (203) hide show
  1. package/README.md +277 -9
  2. package/dist/arxatec-ui.css +1 -1
  3. package/dist/components/autosave_field_status/index.d.ts +5 -0
  4. package/dist/components/autosave_field_status/index.js +12 -0
  5. package/dist/components/autosave_field_status/index.js.map +1 -0
  6. package/dist/components/autosave_status/constants/index.d.ts +1 -0
  7. package/dist/components/autosave_status/constants/index.js +6 -0
  8. package/dist/components/autosave_status/constants/index.js.map +1 -0
  9. package/dist/components/autosave_status/index.d.ts +9 -0
  10. package/dist/components/autosave_status/index.js +46 -0
  11. package/dist/components/autosave_status/index.js.map +1 -0
  12. package/dist/components/autosave_status/types/index.d.ts +2 -0
  13. package/dist/components/editable_fields/context/index.d.ts +31 -0
  14. package/dist/components/editable_fields/context/index.js +30 -0
  15. package/dist/components/editable_fields/context/index.js.map +1 -0
  16. package/dist/components/editable_fields/hooks/index.d.ts +1 -0
  17. package/dist/components/editable_fields/hooks/index.js +8 -0
  18. package/dist/components/editable_fields/hooks/index.js.map +1 -0
  19. package/dist/components/editable_fields/index.d.ts +3 -0
  20. package/dist/components/editable_fields/index.js +32 -0
  21. package/dist/components/editable_fields/index.js.map +1 -0
  22. package/dist/components/emoji_picker/components/emoji_picker_content/index.d.ts +3 -1
  23. package/dist/components/emoji_picker/components/emoji_picker_content/index.js +3 -3
  24. package/dist/components/emoji_picker/components/emoji_picker_content/index.js.map +1 -1
  25. package/dist/components/emoji_picker/components/emoji_picker_footer/index.d.ts +3 -1
  26. package/dist/components/emoji_picker/components/emoji_picker_footer/index.js +3 -3
  27. package/dist/components/emoji_picker/components/emoji_picker_footer/index.js.map +1 -1
  28. package/dist/components/file_explorer/constants/index.d.ts +1 -0
  29. package/dist/components/file_explorer/constants/index.js +6 -0
  30. package/dist/components/file_explorer/constants/index.js.map +1 -0
  31. package/dist/components/file_explorer/explorer_breadcrumb/index.d.ts +9 -0
  32. package/dist/components/file_explorer/explorer_breadcrumb/index.js +21 -0
  33. package/dist/components/file_explorer/explorer_breadcrumb/index.js.map +1 -0
  34. package/dist/components/file_explorer/explorer_empty_state/index.d.ts +9 -0
  35. package/dist/components/file_explorer/explorer_empty_state/index.js +32 -0
  36. package/dist/components/file_explorer/explorer_empty_state/index.js.map +1 -0
  37. package/dist/components/file_explorer/explorer_error_state/index.d.ts +6 -0
  38. package/dist/components/file_explorer/explorer_error_state/index.js +18 -0
  39. package/dist/components/file_explorer/explorer_error_state/index.js.map +1 -0
  40. package/dist/components/file_explorer/explorer_grid/index.d.ts +10 -0
  41. package/dist/components/file_explorer/explorer_grid/index.js +21 -0
  42. package/dist/components/file_explorer/explorer_grid/index.js.map +1 -0
  43. package/dist/components/file_explorer/explorer_grid_card/index.d.ts +25 -0
  44. package/dist/components/file_explorer/explorer_grid_card/index.js +79 -0
  45. package/dist/components/file_explorer/explorer_grid_card/index.js.map +1 -0
  46. package/dist/components/file_explorer/explorer_loading_state/index.d.ts +4 -0
  47. package/dist/components/file_explorer/explorer_loading_state/index.js +9 -0
  48. package/dist/components/file_explorer/explorer_loading_state/index.js.map +1 -0
  49. package/dist/components/file_explorer/explorer_name_cell/index.d.ts +12 -0
  50. package/dist/components/file_explorer/explorer_name_cell/index.js +32 -0
  51. package/dist/components/file_explorer/explorer_name_cell/index.js.map +1 -0
  52. package/dist/components/file_explorer/explorer_panel/index.d.ts +11 -0
  53. package/dist/components/file_explorer/explorer_panel/index.js +17 -0
  54. package/dist/components/file_explorer/explorer_panel/index.js.map +1 -0
  55. package/dist/components/file_explorer/explorer_row/index.d.ts +18 -0
  56. package/dist/components/file_explorer/explorer_row/index.js +39 -0
  57. package/dist/components/file_explorer/explorer_row/index.js.map +1 -0
  58. package/dist/components/file_explorer/explorer_table/index.d.ts +20 -0
  59. package/dist/components/file_explorer/explorer_table/index.js +63 -0
  60. package/dist/components/file_explorer/explorer_table/index.js.map +1 -0
  61. package/dist/components/file_explorer/index.d.ts +26 -0
  62. package/dist/components/file_explorer/item_actions/index.d.ts +3 -0
  63. package/dist/components/file_explorer/item_actions/item_actions_context_content/index.d.ts +5 -0
  64. package/dist/components/file_explorer/item_actions/item_actions_context_content/index.js +21 -0
  65. package/dist/components/file_explorer/item_actions/item_actions_context_content/index.js.map +1 -0
  66. package/dist/components/file_explorer/item_actions/item_actions_dropdown/index.d.ts +7 -0
  67. package/dist/components/file_explorer/item_actions/item_actions_dropdown/index.js +38 -0
  68. package/dist/components/file_explorer/item_actions/item_actions_dropdown/index.js.map +1 -0
  69. package/dist/components/file_explorer/item_actions/types/index.d.ts +9 -0
  70. package/dist/components/file_explorer/item_actions/utils/index.d.ts +2 -0
  71. package/dist/components/file_explorer/item_actions/utils/index.js +6 -0
  72. package/dist/components/file_explorer/item_actions/utils/index.js.map +1 -0
  73. package/dist/components/file_explorer/shared/utils/index.d.ts +2 -0
  74. package/dist/components/file_explorer/shared/utils/index.js +8 -0
  75. package/dist/components/file_explorer/shared/utils/index.js.map +1 -0
  76. package/dist/components/file_explorer/types/index.d.ts +1 -0
  77. package/dist/components/file_thumbnail/index.d.ts +12 -0
  78. package/dist/components/file_thumbnail/index.js +35 -0
  79. package/dist/components/file_thumbnail/index.js.map +1 -0
  80. package/dist/components/file_type_icon/file_kind/index.d.ts +13 -0
  81. package/dist/components/file_type_icon/file_kind/index.js +178 -0
  82. package/dist/components/file_type_icon/file_kind/index.js.map +1 -0
  83. package/dist/components/file_type_icon/file_kind/mime_extensions.d.ts +2 -0
  84. package/dist/components/file_type_icon/file_kind/mime_extensions.js +54 -0
  85. package/dist/components/file_type_icon/file_kind/mime_extensions.js.map +1 -0
  86. package/dist/components/file_type_icon/file_type_icon.d.ts +6 -0
  87. package/dist/components/file_type_icon/file_type_icon.js +67 -0
  88. package/dist/components/file_type_icon/file_type_icon.js.map +1 -0
  89. package/dist/components/file_type_icon/index.d.ts +4 -0
  90. package/dist/components/folder_icon/constants/index.d.ts +11 -0
  91. package/dist/components/folder_icon/constants/index.js +41 -0
  92. package/dist/components/folder_icon/constants/index.js.map +1 -0
  93. package/dist/components/folder_icon/index.d.ts +8 -0
  94. package/dist/components/folder_icon/index.js +31 -0
  95. package/dist/components/folder_icon/index.js.map +1 -0
  96. package/dist/components/hex_color_picker/hex_color_picker_content/index.d.ts +12 -0
  97. package/dist/components/hex_color_picker/hex_color_picker_content/index.js +42 -0
  98. package/dist/components/hex_color_picker/hex_color_picker_content/index.js.map +1 -0
  99. package/dist/components/hex_color_picker/hex_color_picker_popover/index.d.ts +11 -0
  100. package/dist/components/hex_color_picker/hex_color_picker_popover/index.js +35 -0
  101. package/dist/components/hex_color_picker/hex_color_picker_popover/index.js.map +1 -0
  102. package/dist/components/hex_color_picker/index.d.ts +2 -0
  103. package/dist/components/hex_color_picker/utils/index.d.ts +3 -0
  104. package/dist/components/hex_color_picker/utils/index.js +35 -0
  105. package/dist/components/hex_color_picker/utils/index.js.map +1 -0
  106. package/dist/components/icon_emoji_picker/icon_emoji/index.d.ts +5 -0
  107. package/dist/components/icon_emoji_picker/icon_emoji/index.js +19 -0
  108. package/dist/components/icon_emoji_picker/icon_emoji/index.js.map +1 -0
  109. package/dist/components/icon_emoji_picker/icon_emoji_picker/index.d.ts +23 -0
  110. package/dist/components/icon_emoji_picker/icon_emoji_picker/index.js +91 -0
  111. package/dist/components/icon_emoji_picker/icon_emoji_picker/index.js.map +1 -0
  112. package/dist/components/icon_emoji_picker/icon_grid/constants/index.d.ts +3 -0
  113. package/dist/components/icon_emoji_picker/icon_grid/constants/index.js +6 -0
  114. package/dist/components/icon_emoji_picker/icon_grid/constants/index.js.map +1 -0
  115. package/dist/components/icon_emoji_picker/icon_grid/hooks/index.d.ts +1 -0
  116. package/dist/components/icon_emoji_picker/icon_grid/hooks/use_icon_pagination/index.d.ts +7 -0
  117. package/dist/components/icon_emoji_picker/icon_grid/hooks/use_icon_pagination/index.js +35 -0
  118. package/dist/components/icon_emoji_picker/icon_grid/hooks/use_icon_pagination/index.js.map +1 -0
  119. package/dist/components/icon_emoji_picker/icon_grid/index.d.ts +8 -0
  120. package/dist/components/icon_emoji_picker/icon_grid/index.js +49 -0
  121. package/dist/components/icon_emoji_picker/icon_grid/index.js.map +1 -0
  122. package/dist/components/icon_emoji_picker/index.d.ts +3 -0
  123. package/dist/components/icon_emoji_picker/utils/index.d.ts +1 -0
  124. package/dist/components/icon_emoji_picker/utils/index.js +6 -0
  125. package/dist/components/icon_emoji_picker/utils/index.js.map +1 -0
  126. package/dist/components/icons/components/google_calendar_isotype/index.d.ts +2 -0
  127. package/dist/components/icons/components/google_calendar_isotype/index.js +414 -0
  128. package/dist/components/icons/components/google_calendar_isotype/index.js.map +1 -0
  129. package/dist/components/icons/components/google_drive_isotype/index.d.ts +2 -0
  130. package/dist/components/icons/components/google_drive_isotype/index.js +103 -0
  131. package/dist/components/icons/components/google_drive_isotype/index.js.map +1 -0
  132. package/dist/components/icons/components/index.d.ts +4 -0
  133. package/dist/components/icons/components/onedrive_isotype/index.d.ts +2 -0
  134. package/dist/components/icons/components/onedrive_isotype/index.js +246 -0
  135. package/dist/components/icons/components/onedrive_isotype/index.js.map +1 -0
  136. package/dist/components/icons/components/outlook_calendar_isotype/index.d.ts +2 -0
  137. package/dist/components/icons/components/outlook_calendar_isotype/index.js +378 -0
  138. package/dist/components/icons/components/outlook_calendar_isotype/index.js.map +1 -0
  139. package/dist/components/inline_date_field/index.d.ts +11 -0
  140. package/dist/components/inline_date_field/index.js +52 -0
  141. package/dist/components/inline_date_field/index.js.map +1 -0
  142. package/dist/components/inline_duration_field/components/duration_column/index.d.ts +8 -0
  143. package/dist/components/inline_duration_field/components/duration_column/index.js +47 -0
  144. package/dist/components/inline_duration_field/components/duration_column/index.js.map +1 -0
  145. package/dist/components/inline_duration_field/constants/index.d.ts +4 -0
  146. package/dist/components/inline_duration_field/constants/index.js +12 -0
  147. package/dist/components/inline_duration_field/constants/index.js.map +1 -0
  148. package/dist/components/inline_duration_field/index.d.ts +9 -0
  149. package/dist/components/inline_duration_field/index.js +89 -0
  150. package/dist/components/inline_duration_field/index.js.map +1 -0
  151. package/dist/components/inline_duration_field/utils/index.d.ts +11 -0
  152. package/dist/components/inline_duration_field/utils/index.js +21 -0
  153. package/dist/components/inline_duration_field/utils/index.js.map +1 -0
  154. package/dist/components/inline_editable_text/constants/index.d.ts +2 -0
  155. package/dist/components/inline_editable_text/constants/index.js +7 -0
  156. package/dist/components/inline_editable_text/constants/index.js.map +1 -0
  157. package/dist/components/inline_editable_text/index.d.ts +16 -0
  158. package/dist/components/inline_editable_text/index.js +109 -0
  159. package/dist/components/inline_editable_text/index.js.map +1 -0
  160. package/dist/components/inline_field_trigger/index.d.ts +3 -0
  161. package/dist/components/inline_field_trigger/index.js +16 -0
  162. package/dist/components/inline_field_trigger/index.js.map +1 -0
  163. package/dist/components/inline_markdown_field/index.d.ts +14 -0
  164. package/dist/components/inline_markdown_field/index.js +70 -0
  165. package/dist/components/inline_markdown_field/index.js.map +1 -0
  166. package/dist/components/inline_option_field/index.d.ts +19 -0
  167. package/dist/components/inline_option_field/index.js +56 -0
  168. package/dist/components/inline_option_field/index.js.map +1 -0
  169. package/dist/components/offline_banner/index.d.ts +12 -0
  170. package/dist/components/offline_banner/index.js +76 -0
  171. package/dist/components/offline_banner/index.js.map +1 -0
  172. package/dist/components/property_row/index.d.ts +10 -0
  173. package/dist/components/property_row/index.js +46 -0
  174. package/dist/components/property_row/index.js.map +1 -0
  175. package/dist/components/select_table_header/index.d.ts +13 -0
  176. package/dist/components/select_table_header/index.js +17 -0
  177. package/dist/components/select_table_header/index.js.map +1 -0
  178. package/dist/components/shiny_text/index.d.ts +7 -0
  179. package/dist/components/shiny_text/index.js +22 -0
  180. package/dist/components/shiny_text/index.js.map +1 -0
  181. package/dist/components/table_date_field/index.d.ts +8 -0
  182. package/dist/components/table_date_field/index.js +67 -0
  183. package/dist/components/table_date_field/index.js.map +1 -0
  184. package/dist/components/table_option_field/index.d.ts +13 -0
  185. package/dist/components/table_option_field/index.js +73 -0
  186. package/dist/components/table_option_field/index.js.map +1 -0
  187. package/dist/editable-fields.js +19 -0
  188. package/dist/exports/editable-fields.d.ts +19 -0
  189. package/dist/exports/icon-emoji-picker.d.ts +1 -0
  190. package/dist/exports/offline-banner.d.ts +1 -0
  191. package/dist/exports/provider-icons.d.ts +4 -0
  192. package/dist/exports/shiny-text.d.ts +1 -0
  193. package/dist/icon-emoji-picker.js +4 -0
  194. package/dist/index.d.ts +8 -0
  195. package/dist/index.js +85 -40
  196. package/dist/offline-banner.js +2 -0
  197. package/dist/provider-icons.js +5 -0
  198. package/dist/shiny-text.js +2 -0
  199. package/dist/styles.css +40 -1
  200. package/dist/utilities/colors/index.d.ts +1 -0
  201. package/dist/utilities/colors/index.js +9 -0
  202. package/dist/utilities/colors/index.js.map +1 -0
  203. package/package.json +32 -3
@@ -0,0 +1,89 @@
1
+ import { cn as e } from "../../utilities/class/index.js";
2
+ import { Popover as t, PopoverContent as n, PopoverTrigger as r } from "../popover/index.js";
3
+ import { useEditableFields as i } from "../editable_fields/hooks/index.js";
4
+ import { INLINE_FIELD_ROW as a } from "../inline_editable_text/constants/index.js";
5
+ import { DURATION_PRESETS_SECONDS as o } from "./constants/index.js";
6
+ import { durationToParts as s, formatDurationLabel as c, hourValues as l, minuteValues as u, partsToSeconds as d } from "./utils/index.js";
7
+ import { DurationColumn as f } from "./components/duration_column/index.js";
8
+ import { useState as p } from "react";
9
+ import { jsx as m, jsxs as h } from "react/jsx-runtime";
10
+ //#region src/components/inline_duration_field/index.tsx
11
+ var g = ({ value: g, onChange: _, ariaLabel: v, emptyText: y, testId: b, disabled: x }) => {
12
+ let { labels: S } = i(), [C, w] = p(!1), { hours: T, minutes: E } = s(g), D = c(g), O = g <= 0, k = S.hours, A = S.minutes, j = (e, t) => {
13
+ _(d(e, t));
14
+ };
15
+ return /* @__PURE__ */ h(t, {
16
+ open: C,
17
+ onOpenChange: w,
18
+ children: [/* @__PURE__ */ m(r, {
19
+ asChild: !0,
20
+ children: /* @__PURE__ */ m("button", {
21
+ type: "button",
22
+ title: v,
23
+ "aria-label": v,
24
+ "aria-expanded": C,
25
+ "data-testid": b,
26
+ disabled: x,
27
+ className: e(a, "w-auto px-2 tabular-nums disabled:pointer-events-none disabled:opacity-50", O && "text-muted-foreground"),
28
+ children: O ? y : D
29
+ })
30
+ }), /* @__PURE__ */ h(n, {
31
+ align: "start",
32
+ className: "w-auto overflow-hidden p-0",
33
+ onWheel: (e) => e.stopPropagation(),
34
+ children: [
35
+ /* @__PURE__ */ m("div", {
36
+ className: "flex gap-1 border-b border-border px-2 py-2",
37
+ children: o.map((t) => /* @__PURE__ */ m("button", {
38
+ type: "button",
39
+ onClick: () => {
40
+ _(t), w(!1);
41
+ },
42
+ className: e("rounded-md px-2 py-1 text-xs tabular-nums text-muted-foreground hover:bg-accent hover:text-foreground", g === t && "bg-accent font-medium text-accent-foreground"),
43
+ children: c(t)
44
+ }, t))
45
+ }),
46
+ /* @__PURE__ */ h("div", {
47
+ className: "grid grid-cols-[1fr_auto_1fr] items-center gap-x-1 px-2 pt-2",
48
+ children: [
49
+ /* @__PURE__ */ m("p", {
50
+ className: "text-center text-xs font-medium text-muted-foreground",
51
+ children: k
52
+ }),
53
+ /* @__PURE__ */ m("span", {}),
54
+ /* @__PURE__ */ m("p", {
55
+ className: "text-center text-xs font-medium text-muted-foreground",
56
+ children: A
57
+ })
58
+ ]
59
+ }),
60
+ /* @__PURE__ */ h("div", {
61
+ className: "grid grid-cols-[1fr_auto_1fr] items-center gap-x-1 px-2 pb-2",
62
+ children: [
63
+ /* @__PURE__ */ m(f, {
64
+ ariaLabel: k,
65
+ values: l(T),
66
+ selected: T,
67
+ onChange: (e) => j(e, E)
68
+ }),
69
+ /* @__PURE__ */ m("span", {
70
+ "aria-hidden": !0,
71
+ className: "text-lg font-medium text-muted-foreground",
72
+ children: ":"
73
+ }),
74
+ /* @__PURE__ */ m(f, {
75
+ ariaLabel: A,
76
+ values: u(),
77
+ selected: E,
78
+ onChange: (e) => j(T, e)
79
+ })
80
+ ]
81
+ })
82
+ ]
83
+ })]
84
+ });
85
+ };
86
+ //#endregion
87
+ export { g as InlineDurationField };
88
+
89
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/inline_duration_field/index.tsx"],"sourcesContent":["import { useEditableFields } from \"@/components/editable_fields/hooks\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"@/components/popover\";\nimport { useState } from \"react\";\nimport { cn } from \"@/utilities/class\";\nimport { INLINE_FIELD_ROW } from \"../inline_editable_text/constants\";\nimport { DurationColumn } from \"./components/duration_column\";\nimport { DURATION_PRESETS_SECONDS } from \"./constants\";\nimport {\n durationToParts,\n formatDurationLabel,\n hourValues,\n minuteValues,\n partsToSeconds,\n} from \"./utils\";\n\nexport interface InlineDurationFieldProps {\n value: number;\n onChange: (seconds: number) => void;\n ariaLabel: string;\n emptyText: string;\n testId?: string;\n disabled?: boolean;\n}\n\nexport const InlineDurationField = ({\n value,\n onChange,\n ariaLabel,\n emptyText,\n testId,\n disabled,\n}: InlineDurationFieldProps) => {\n const { labels } = useEditableFields();\n const [open, setOpen] = useState(false);\n const { hours, minutes } = durationToParts(value);\n const label = formatDurationLabel(value);\n const isEmpty = value <= 0;\n const hoursLabel = labels.hours;\n const minutesLabel = labels.minutes;\n\n const commit = (nextHours: number, nextMinutes: number) => {\n onChange(partsToSeconds(nextHours, nextMinutes));\n };\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n title={ariaLabel}\n aria-label={ariaLabel}\n aria-expanded={open}\n data-testid={testId}\n disabled={disabled}\n className={cn(\n INLINE_FIELD_ROW,\n \"w-auto px-2 tabular-nums disabled:pointer-events-none disabled:opacity-50\",\n isEmpty && \"text-muted-foreground\",\n )}\n >\n {isEmpty ? emptyText : label}\n </button>\n </PopoverTrigger>\n <PopoverContent\n align=\"start\"\n className=\"w-auto overflow-hidden p-0\"\n onWheel={(event) => event.stopPropagation()}\n >\n <div className=\"flex gap-1 border-b border-border px-2 py-2\">\n {DURATION_PRESETS_SECONDS.map((preset) => (\n <button\n key={preset}\n type=\"button\"\n onClick={() => {\n onChange(preset);\n setOpen(false);\n }}\n className={cn(\n \"rounded-md px-2 py-1 text-xs tabular-nums text-muted-foreground hover:bg-accent hover:text-foreground\",\n value === preset &&\n \"bg-accent font-medium text-accent-foreground\",\n )}\n >\n {formatDurationLabel(preset)}\n </button>\n ))}\n </div>\n\n <div className=\"grid grid-cols-[1fr_auto_1fr] items-center gap-x-1 px-2 pt-2\">\n <p className=\"text-center text-xs font-medium text-muted-foreground\">\n {hoursLabel}\n </p>\n <span />\n <p className=\"text-center text-xs font-medium text-muted-foreground\">\n {minutesLabel}\n </p>\n </div>\n\n <div className=\"grid grid-cols-[1fr_auto_1fr] items-center gap-x-1 px-2 pb-2\">\n <DurationColumn\n ariaLabel={hoursLabel}\n values={hourValues(hours)}\n selected={hours}\n onChange={(next) => commit(next, minutes)}\n />\n <span\n aria-hidden\n className=\"text-lg font-medium text-muted-foreground\"\n >\n :\n </span>\n <DurationColumn\n ariaLabel={minutesLabel}\n values={minuteValues()}\n selected={minutes}\n onChange={(next) => commit(hours, next)}\n />\n </div>\n </PopoverContent>\n </Popover>\n );\n};\n"],"mappings":";;;;;;;;;;AAwBA,IAAa,KAAuB,EAClC,UACA,aACA,cACA,cACA,WACA,kBAC8B;CAC9B,IAAM,EAAE,cAAW,GAAmB,EAChC,CAAC,GAAM,KAAW,EAAS,GAAM,EACjC,EAAE,UAAO,eAAY,EAAgB,EAAM,EAC3C,IAAQ,EAAoB,EAAM,EAClC,IAAU,KAAS,GACnB,IAAa,EAAO,OACpB,IAAe,EAAO,SAEtB,KAAU,GAAmB,MAAwB;AACzD,IAAS,EAAe,GAAW,EAAY,CAAC;;AAGlD,QACE,kBAAC,GAAD;EAAe;EAAM,cAAc;YAAnC,CACE,kBAAC,GAAD;GAAgB,SAAA;aACd,kBAAC,UAAD;IACE,MAAK;IACL,OAAO;IACP,cAAY;IACZ,iBAAe;IACf,eAAa;IACH;IACV,WAAW,EACT,GACA,6EACA,KAAW,wBACZ;cAEA,IAAU,IAAY;IAChB,CAAA;GACM,CAAA,EACjB,kBAAC,GAAD;GACE,OAAM;GACN,WAAU;GACV,UAAU,MAAU,EAAM,iBAAiB;aAH7C;IAKE,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAyB,KAAK,MAC7B,kBAAC,UAAD;MAEE,MAAK;MACL,eAAe;AAEb,OADA,EAAS,EAAO,EAChB,EAAQ,GAAM;;MAEhB,WAAW,EACT,yGACA,MAAU,KACR,+CACH;gBAEA,EAAoB,EAAO;MACrB,EAbF,EAaE,CACT;KACE,CAAA;IAEN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,KAAD;OAAG,WAAU;iBACV;OACC,CAAA;MACJ,kBAAC,QAAD,EAAQ,CAAA;MACR,kBAAC,KAAD;OAAG,WAAU;iBACV;OACC,CAAA;MACA;;IAEN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OACE,WAAW;OACX,QAAQ,EAAW,EAAM;OACzB,UAAU;OACV,WAAW,MAAS,EAAO,GAAM,EAAQ;OACzC,CAAA;MACF,kBAAC,QAAD;OACE,eAAA;OACA,WAAU;iBACX;OAEM,CAAA;MACP,kBAAC,GAAD;OACE,WAAW;OACX,QAAQ,GAAc;OACtB,UAAU;OACV,WAAW,MAAS,EAAO,GAAO,EAAK;OACvC,CAAA;MACE;;IACS;KACT"}
@@ -0,0 +1,11 @@
1
+ export declare const durationToParts: (seconds: number) => {
2
+ hours: number;
3
+ minutes: number;
4
+ };
5
+ export declare const partsToSeconds: (hours: number, minutes: number) => number;
6
+ export declare const formatDurationLabel: (seconds: number) => string;
7
+ export declare const hourValues: (currentHours?: number) => number[];
8
+ export declare const minuteValues: () => number[];
9
+ export declare const SCROLL_AREA_VIEWPORT_SELECTOR = "[data-slot=scroll-area-viewport]";
10
+ export declare const getScrollViewport: (root: HTMLElement) => HTMLElement;
11
+ export declare const attachWheelScroll: (node: HTMLElement) => () => void;
@@ -0,0 +1,21 @@
1
+ //#region src/components/inline_duration_field/utils/index.ts
2
+ var e = (e) => {
3
+ let t = Math.max(0, Math.floor(e));
4
+ return {
5
+ hours: Math.floor(t / 3600),
6
+ minutes: Math.floor(t % 3600 / 60)
7
+ };
8
+ }, t = (e, t) => Math.max(0, e) * 3600 + Math.max(0, t) * 60, n = (t) => {
9
+ if (t <= 0) return "";
10
+ let { hours: n, minutes: r } = e(t);
11
+ return n > 0 && r > 0 ? `${n}h ${r}m` : n > 0 ? `${n}h` : `${r}m`;
12
+ }, r = (e = 0) => Array.from({ length: Math.max(23, e) + 1 }, (e, t) => t), i = () => Array.from({ length: 60 }, (e, t) => t), a = (e) => e.querySelector("[data-slot=scroll-area-viewport]") ?? e, o = (e) => {
13
+ let t = (t) => {
14
+ t.preventDefault(), t.stopPropagation(), e.scrollTop += t.deltaY;
15
+ };
16
+ return e.addEventListener("wheel", t, { passive: !1 }), () => e.removeEventListener("wheel", t);
17
+ };
18
+ //#endregion
19
+ export { o as attachWheelScroll, e as durationToParts, n as formatDurationLabel, a as getScrollViewport, r as hourValues, i as minuteValues, t as partsToSeconds };
20
+
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/inline_duration_field/utils/index.ts"],"sourcesContent":["import { DURATION_MAX_HOURS, DURATION_MAX_MINUTES } from \"../constants\";\n\nexport const durationToParts = (seconds: number) => {\n const safe = Math.max(0, Math.floor(seconds));\n return {\n hours: Math.floor(safe / 3600),\n minutes: Math.floor((safe % 3600) / 60),\n };\n};\n\nexport const partsToSeconds = (hours: number, minutes: number) =>\n Math.max(0, hours) * 3600 + Math.max(0, minutes) * 60;\n\nexport const formatDurationLabel = (seconds: number) => {\n if (seconds <= 0) return \"\";\n const { hours, minutes } = durationToParts(seconds);\n if (hours > 0 && minutes > 0) return `${hours}h ${minutes}m`;\n if (hours > 0) return `${hours}h`;\n return `${minutes}m`;\n};\n\nexport const hourValues = (currentHours = 0) =>\n Array.from(\n { length: Math.max(DURATION_MAX_HOURS, currentHours) + 1 },\n (_, index) => index,\n );\n\nexport const minuteValues = () =>\n Array.from({ length: DURATION_MAX_MINUTES + 1 }, (_, index) => index);\n\nexport const SCROLL_AREA_VIEWPORT_SELECTOR = \"[data-slot=scroll-area-viewport]\";\n\nexport const getScrollViewport = (root: HTMLElement) =>\n root.querySelector<HTMLElement>(SCROLL_AREA_VIEWPORT_SELECTOR) ?? root;\n\nexport const attachWheelScroll = (node: HTMLElement) => {\n const onWheel = (event: WheelEvent) => {\n event.preventDefault();\n event.stopPropagation();\n node.scrollTop += event.deltaY;\n };\n\n node.addEventListener(\"wheel\", onWheel, { passive: false });\n return () => node.removeEventListener(\"wheel\", onWheel);\n};\n"],"mappings":";AAEA,IAAa,KAAmB,MAAoB;CAClD,IAAM,IAAO,KAAK,IAAI,GAAG,KAAK,MAAM,EAAQ,CAAC;AAC7C,QAAO;EACL,OAAO,KAAK,MAAM,IAAO,KAAK;EAC9B,SAAS,KAAK,MAAO,IAAO,OAAQ,GAAG;EACxC;GAGU,KAAkB,GAAe,MAC5C,KAAK,IAAI,GAAG,EAAM,GAAG,OAAO,KAAK,IAAI,GAAG,EAAQ,GAAG,IAExC,KAAuB,MAAoB;AACtD,KAAI,KAAW,EAAG,QAAO;CACzB,IAAM,EAAE,UAAO,eAAY,EAAgB,EAAQ;AAGnD,QAFI,IAAQ,KAAK,IAAU,IAAU,GAAG,EAAM,IAAI,EAAQ,KACtD,IAAQ,IAAU,GAAG,EAAM,KACxB,GAAG,EAAQ;GAGP,KAAc,IAAe,MACxC,MAAM,KACJ,EAAE,QAAQ,KAAK,IAAA,IAAwB,EAAa,GAAG,GAAG,GACzD,GAAG,MAAU,EACf,EAEU,UACX,MAAM,KAAK,EAAE,QAAA,IAAkC,GAAG,GAAG,MAAU,EAAM,EAI1D,KAAqB,MAChC,EAAK,cAAA,mCAAyD,IAAI,GAEvD,KAAqB,MAAsB;CACtD,IAAM,KAAW,MAAsB;AAGrC,EAFA,EAAM,gBAAgB,EACtB,EAAM,iBAAiB,EACvB,EAAK,aAAa,EAAM;;AAI1B,QADA,EAAK,iBAAiB,SAAS,GAAS,EAAE,SAAS,IAAO,CAAC,QAC9C,EAAK,oBAAoB,SAAS,EAAQ"}
@@ -0,0 +1,2 @@
1
+ export declare const SHARED_BOX = "-mx-2 w-full rounded-md border px-2 py-1 text-left text-sm";
2
+ export declare const INLINE_FIELD_ROW: string;
@@ -0,0 +1,7 @@
1
+ import { cn as e } from "../../../utilities/class/index.js";
2
+ //#region src/components/inline_editable_text/constants/index.ts
3
+ var t = "-mx-2 w-full rounded-md border px-2 py-1 text-left text-sm", n = e(t, "min-h-8 border-transparent transition-colors", "hover:border-input hover:bg-muted/40", "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none");
4
+ //#endregion
5
+ export { n as INLINE_FIELD_ROW, t as SHARED_BOX };
6
+
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/inline_editable_text/constants/index.ts"],"sourcesContent":["import { cn } from \"@/utilities/class\";\n\nexport const SHARED_BOX =\n \"-mx-2 w-full rounded-md border px-2 py-1 text-left text-sm\";\n\nexport const INLINE_FIELD_ROW = cn(\n SHARED_BOX,\n \"min-h-8 border-transparent transition-colors\",\n \"hover:border-input hover:bg-muted/40\",\n \"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none\",\n);\n"],"mappings":";;AAEA,IAAa,IACX,8DAEW,IAAmB,EAC9B,GACA,gDACA,wCACA,2GACD"}
@@ -0,0 +1,16 @@
1
+ export interface InlineEditableTextProps {
2
+ value: string;
3
+ onCommit: (value: string) => void;
4
+ onChange?: (value: string) => void;
5
+ onCancel?: () => void;
6
+ ariaLabel: string;
7
+ placeholder?: string;
8
+ emptyText?: string;
9
+ multiline?: boolean;
10
+ disabled?: boolean;
11
+ validate?: (value: string) => string | null;
12
+ selectOnEdit?: boolean;
13
+ className?: string;
14
+ testId?: string;
15
+ }
16
+ export declare function InlineEditableText({ value, onCommit, onChange, onCancel, ariaLabel, placeholder, emptyText, multiline, disabled, validate, selectOnEdit, className, testId, }: InlineEditableTextProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,109 @@
1
+ import { cn as e } from "../../utilities/class/index.js";
2
+ import { useEditableFields as t } from "../editable_fields/hooks/index.js";
3
+ import { INLINE_FIELD_ROW as n, SHARED_BOX as r } from "./constants/index.js";
4
+ import { useLayoutEffect as i, useRef as a, useState as o } from "react";
5
+ import { jsx as s, jsxs as c } from "react/jsx-runtime";
6
+ //#region src/components/inline_editable_text/index.tsx
7
+ var l = 320;
8
+ function u(e) {
9
+ e.style.height = "auto", e.style.height = `${Math.min(e.scrollHeight, l)}px`;
10
+ }
11
+ function d({ value: l, onCommit: d, onChange: f, onCancel: p, ariaLabel: m, placeholder: h, emptyText: g, multiline: _ = !1, disabled: v = !1, validate: y, selectOnEdit: b = !1, className: x, testId: S }) {
12
+ let { labels: C } = t(), [w, T] = o(!1), [E, D] = o(l), [O, k] = o(null), A = a(null), j = a(!1);
13
+ i(() => {
14
+ if (!w) return;
15
+ let e = A.current;
16
+ if (e) {
17
+ if (e.focus(), b) e.select();
18
+ else {
19
+ let t = e.value.length;
20
+ e.setSelectionRange(t, t);
21
+ }
22
+ e instanceof HTMLTextAreaElement && u(e);
23
+ }
24
+ }, [w, b]);
25
+ let M = () => {
26
+ v || (D(l), k(null), T(!0));
27
+ }, N = () => {
28
+ j.current = !0, p?.(), D(l), k(null), T(!1);
29
+ }, P = () => {
30
+ let e = E.trim(), t = y?.(e) ?? null;
31
+ if (t !== null) {
32
+ k(t), j.current = !0, A.current?.focus();
33
+ return;
34
+ }
35
+ T(!1), k(null), e !== l && (D(e), d(e));
36
+ }, F = (e) => {
37
+ D(e), f?.(e);
38
+ }, I = (e) => {
39
+ if (e.key === "Escape") {
40
+ e.preventDefault(), e.stopPropagation(), N();
41
+ return;
42
+ }
43
+ e.key === "Enter" && (_ && !e.metaKey && !e.ctrlKey || (e.preventDefault(), P()));
44
+ }, L = () => {
45
+ if (j.current) {
46
+ j.current = !1;
47
+ return;
48
+ }
49
+ P();
50
+ };
51
+ if (w) {
52
+ let t = e(r, "border-input bg-card outline-none transition-colors", "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50", O && "border-destructive", x);
53
+ return /* @__PURE__ */ c("div", { children: [
54
+ _ ? /* @__PURE__ */ s("textarea", {
55
+ ref: (e) => {
56
+ A.current = e;
57
+ },
58
+ "aria-label": m,
59
+ "aria-invalid": O !== null,
60
+ "data-testid": S,
61
+ rows: 1,
62
+ value: E,
63
+ placeholder: h ?? C.empty,
64
+ className: e(t, "resize-none overflow-y-auto"),
65
+ onChange: (e) => {
66
+ F(e.target.value), u(e.target);
67
+ },
68
+ onKeyDown: I,
69
+ onBlur: L
70
+ }) : /* @__PURE__ */ s("input", {
71
+ ref: (e) => {
72
+ A.current = e;
73
+ },
74
+ "aria-label": m,
75
+ "aria-invalid": O !== null,
76
+ "data-testid": S,
77
+ value: E,
78
+ placeholder: h ?? C.empty,
79
+ className: t,
80
+ onChange: (e) => F(e.target.value),
81
+ onKeyDown: I,
82
+ onBlur: L
83
+ }),
84
+ O ? /* @__PURE__ */ s("p", {
85
+ className: "mt-1 text-xs text-destructive",
86
+ "data-testid": S ? `${S}-error` : void 0,
87
+ children: O
88
+ }) : null,
89
+ /* @__PURE__ */ s("p", {
90
+ className: "sr-only",
91
+ children: C.hint
92
+ })
93
+ ] });
94
+ }
95
+ let R = l.trim() === "";
96
+ return /* @__PURE__ */ s("button", {
97
+ type: "button",
98
+ "aria-label": m,
99
+ "data-testid": S,
100
+ disabled: v,
101
+ onClick: M,
102
+ className: e(n, "disabled:pointer-events-none disabled:opacity-50", _ ? "whitespace-pre-wrap break-words" : "truncate", R && "text-muted-foreground", x),
103
+ children: R ? g ?? h ?? C.empty : l
104
+ });
105
+ }
106
+ //#endregion
107
+ export { d as InlineEditableText };
108
+
109
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/inline_editable_text/index.tsx"],"sourcesContent":["import { useEditableFields } from \"@/components/editable_fields/hooks\";\nimport {\n useLayoutEffect,\n useRef,\n useState,\n type KeyboardEvent as ReactKeyboardEvent,\n} from \"react\";\nimport { cn } from \"@/utilities/class\";\nimport { INLINE_FIELD_ROW, SHARED_BOX } from \"./constants\";\n\nexport interface InlineEditableTextProps {\n value: string;\n onCommit: (value: string) => void;\n onChange?: (value: string) => void;\n onCancel?: () => void;\n ariaLabel: string;\n placeholder?: string;\n emptyText?: string;\n multiline?: boolean;\n disabled?: boolean;\n validate?: (value: string) => string | null;\n selectOnEdit?: boolean;\n className?: string;\n testId?: string;\n}\n\nconst TEXTAREA_MAX_HEIGHT_PX = 320;\n\nfunction resizeToContent(element: HTMLTextAreaElement) {\n element.style.height = \"auto\";\n element.style.height = `${Math.min(element.scrollHeight, TEXTAREA_MAX_HEIGHT_PX)}px`;\n}\n\nexport function InlineEditableText({\n value,\n onCommit,\n onChange,\n onCancel,\n ariaLabel,\n placeholder,\n emptyText,\n multiline = false,\n disabled = false,\n validate,\n selectOnEdit = false,\n className,\n testId,\n}: InlineEditableTextProps) {\n const { labels } = useEditableFields();\n\n const [isEditing, setIsEditing] = useState(false);\n const [draft, setDraft] = useState(value);\n const [error, setError] = useState<string | null>(null);\n\n const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement | null>(null);\n const skipNextBlurRef = useRef(false);\n\n useLayoutEffect(() => {\n if (!isEditing) return;\n const element = inputRef.current;\n if (!element) return;\n\n element.focus();\n if (selectOnEdit) {\n element.select();\n } else {\n const end = element.value.length;\n element.setSelectionRange(end, end);\n }\n if (element instanceof HTMLTextAreaElement) resizeToContent(element);\n }, [isEditing, selectOnEdit]);\n\n const startEditing = () => {\n if (disabled) return;\n setDraft(value);\n setError(null);\n setIsEditing(true);\n };\n\n const cancel = () => {\n skipNextBlurRef.current = true;\n onCancel?.();\n setDraft(value);\n setError(null);\n setIsEditing(false);\n };\n\n const commit = () => {\n const next = draft.trim();\n\n const invalid = validate?.(next) ?? null;\n if (invalid !== null) {\n setError(invalid);\n skipNextBlurRef.current = true;\n inputRef.current?.focus();\n return;\n }\n\n setIsEditing(false);\n setError(null);\n if (next === value) return;\n setDraft(next);\n onCommit(next);\n };\n\n const handleChange = (next: string) => {\n setDraft(next);\n onChange?.(next);\n };\n\n const onKeyDown = (\n event: ReactKeyboardEvent<HTMLInputElement | HTMLTextAreaElement>,\n ) => {\n if (event.key === \"Escape\") {\n event.preventDefault();\n event.stopPropagation();\n cancel();\n return;\n }\n\n if (event.key !== \"Enter\") return;\n\n if (multiline && !event.metaKey && !event.ctrlKey) return;\n\n event.preventDefault();\n commit();\n };\n\n const onBlur = () => {\n if (skipNextBlurRef.current) {\n skipNextBlurRef.current = false;\n return;\n }\n commit();\n };\n\n if (isEditing) {\n const editClassName = cn(\n SHARED_BOX,\n \"border-input bg-card outline-none transition-colors\",\n \"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50\",\n error && \"border-destructive\",\n className,\n );\n\n return (\n <div>\n {multiline ? (\n <textarea\n ref={(node) => {\n inputRef.current = node;\n }}\n aria-label={ariaLabel}\n aria-invalid={error !== null}\n data-testid={testId}\n rows={1}\n value={draft}\n placeholder={placeholder ?? labels.empty}\n className={cn(editClassName, \"resize-none overflow-y-auto\")}\n onChange={(event) => {\n handleChange(event.target.value);\n resizeToContent(event.target);\n }}\n onKeyDown={onKeyDown}\n onBlur={onBlur}\n />\n ) : (\n <input\n ref={(node) => {\n inputRef.current = node;\n }}\n aria-label={ariaLabel}\n aria-invalid={error !== null}\n data-testid={testId}\n value={draft}\n placeholder={placeholder ?? labels.empty}\n className={editClassName}\n onChange={(event) => handleChange(event.target.value)}\n onKeyDown={onKeyDown}\n onBlur={onBlur}\n />\n )}\n {error ? (\n <p\n className=\"mt-1 text-xs text-destructive\"\n data-testid={testId ? `${testId}-error` : undefined}\n >\n {error}\n </p>\n ) : null}\n <p className=\"sr-only\">{labels.hint}</p>\n </div>\n );\n }\n\n const isEmpty = value.trim() === \"\";\n\n return (\n <button\n type=\"button\"\n aria-label={ariaLabel}\n data-testid={testId}\n disabled={disabled}\n onClick={startEditing}\n className={cn(\n INLINE_FIELD_ROW,\n \"disabled:pointer-events-none disabled:opacity-50\",\n multiline ? \"whitespace-pre-wrap break-words\" : \"truncate\",\n isEmpty && \"text-muted-foreground\",\n className,\n )}\n >\n {isEmpty ? (emptyText ?? placeholder ?? labels.empty) : value}\n </button>\n );\n}\n"],"mappings":";;;;;;AA0BA,IAAM,IAAyB;AAE/B,SAAS,EAAgB,GAA8B;AAErD,CADA,EAAQ,MAAM,SAAS,QACvB,EAAQ,MAAM,SAAS,GAAG,KAAK,IAAI,EAAQ,cAAc,EAAuB,CAAC;;AAGnF,SAAgB,EAAmB,EACjC,UACA,aACA,aACA,aACA,cACA,gBACA,cACA,eAAY,IACZ,cAAW,IACX,aACA,kBAAe,IACf,cACA,aAC0B;CAC1B,IAAM,EAAE,cAAW,GAAmB,EAEhC,CAAC,GAAW,KAAgB,EAAS,GAAM,EAC3C,CAAC,GAAO,KAAY,EAAS,EAAM,EACnC,CAAC,GAAO,KAAY,EAAwB,KAAK,EAEjD,IAAW,EAAsD,KAAK,EACtE,IAAkB,EAAO,GAAM;AAErC,SAAsB;AACpB,MAAI,CAAC,EAAW;EAChB,IAAM,IAAU,EAAS;AACpB,SAGL;OADA,EAAQ,OAAO,EACX,EACF,GAAQ,QAAQ;QACX;IACL,IAAM,IAAM,EAAQ,MAAM;AAC1B,MAAQ,kBAAkB,GAAK,EAAI;;AAErC,GAAI,aAAmB,uBAAqB,EAAgB,EAAQ;;IACnE,CAAC,GAAW,EAAa,CAAC;CAE7B,IAAM,UAAqB;AACrB,QACJ,EAAS,EAAM,EACf,EAAS,KAAK,EACd,EAAa,GAAK;IAGd,UAAe;AAKnB,EAJA,EAAgB,UAAU,IAC1B,KAAY,EACZ,EAAS,EAAM,EACf,EAAS,KAAK,EACd,EAAa,GAAM;IAGf,UAAe;EACnB,IAAM,IAAO,EAAM,MAAM,EAEnB,IAAU,IAAW,EAAK,IAAI;AACpC,MAAI,MAAY,MAAM;AAGpB,GAFA,EAAS,EAAQ,EACjB,EAAgB,UAAU,IAC1B,EAAS,SAAS,OAAO;AACzB;;AAGF,IAAa,GAAM,EACnB,EAAS,KAAK,EACV,MAAS,MACb,EAAS,EAAK,EACd,EAAS,EAAK;IAGV,KAAgB,MAAiB;AAErC,EADA,EAAS,EAAK,EACd,IAAW,EAAK;IAGZ,KACJ,MACG;AACH,MAAI,EAAM,QAAQ,UAAU;AAG1B,GAFA,EAAM,gBAAgB,EACtB,EAAM,iBAAiB,EACvB,GAAQ;AACR;;AAGE,IAAM,QAAQ,YAEd,KAAa,CAAC,EAAM,WAAW,CAAC,EAAM,YAE1C,EAAM,gBAAgB,EACtB,GAAQ;IAGJ,UAAe;AACnB,MAAI,EAAgB,SAAS;AAC3B,KAAgB,UAAU;AAC1B;;AAEF,KAAQ;;AAGV,KAAI,GAAW;EACb,IAAM,IAAgB,EACpB,GACA,uDACA,iFACA,KAAS,sBACT,EACD;AAED,SACE,kBAAC,OAAD,EAAA,UAAA;GACG,IACC,kBAAC,YAAD;IACE,MAAM,MAAS;AACb,OAAS,UAAU;;IAErB,cAAY;IACZ,gBAAc,MAAU;IACxB,eAAa;IACb,MAAM;IACN,OAAO;IACP,aAAa,KAAe,EAAO;IACnC,WAAW,EAAG,GAAe,8BAA8B;IAC3D,WAAW,MAAU;AAEnB,KADA,EAAa,EAAM,OAAO,MAAM,EAChC,EAAgB,EAAM,OAAO;;IAEpB;IACH;IACR,CAAA,GAEF,kBAAC,SAAD;IACE,MAAM,MAAS;AACb,OAAS,UAAU;;IAErB,cAAY;IACZ,gBAAc,MAAU;IACxB,eAAa;IACb,OAAO;IACP,aAAa,KAAe,EAAO;IACnC,WAAW;IACX,WAAW,MAAU,EAAa,EAAM,OAAO,MAAM;IAC1C;IACH;IACR,CAAA;GAEH,IACC,kBAAC,KAAD;IACE,WAAU;IACV,eAAa,IAAS,GAAG,EAAO,UAAU,KAAA;cAEzC;IACC,CAAA,GACF;GACJ,kBAAC,KAAD;IAAG,WAAU;cAAW,EAAO;IAAS,CAAA;GACpC,EAAA,CAAA;;CAIV,IAAM,IAAU,EAAM,MAAM,KAAK;AAEjC,QACE,kBAAC,UAAD;EACE,MAAK;EACL,cAAY;EACZ,eAAa;EACH;EACV,SAAS;EACT,WAAW,EACT,GACA,oDACA,IAAY,oCAAoC,YAChD,KAAW,yBACX,EACD;YAEA,IAAW,KAAa,KAAe,EAAO,QAAS;EACjD,CAAA"}
@@ -0,0 +1,3 @@
1
+ import { ButtonHTMLAttributes } from 'react';
2
+ export type InlineFieldTriggerProps = ButtonHTMLAttributes<HTMLButtonElement>;
3
+ export declare const InlineFieldTrigger: import('react').ForwardRefExoticComponent<InlineFieldTriggerProps & import('react').RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,16 @@
1
+ import { cn as e } from "../../utilities/class/index.js";
2
+ import { INLINE_FIELD_ROW as t } from "../inline_editable_text/constants/index.js";
3
+ import { forwardRef as n } from "react";
4
+ import { jsx as r } from "react/jsx-runtime";
5
+ //#region src/components/inline_field_trigger/index.tsx
6
+ var i = n(({ className: n, type: i = "button", ...a }, o) => /* @__PURE__ */ r("button", {
7
+ ref: o,
8
+ type: i,
9
+ className: e(t, n),
10
+ ...a
11
+ }));
12
+ i.displayName = "InlineFieldTrigger";
13
+ //#endregion
14
+ export { i as InlineFieldTrigger };
15
+
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/inline_field_trigger/index.tsx"],"sourcesContent":["import { forwardRef, type ButtonHTMLAttributes } from \"react\";\nimport { cn } from \"@/utilities/class\";\nimport { INLINE_FIELD_ROW } from \"../inline_editable_text/constants\";\n\nexport type InlineFieldTriggerProps = ButtonHTMLAttributes<HTMLButtonElement>;\n\nexport const InlineFieldTrigger = forwardRef<\n HTMLButtonElement,\n InlineFieldTriggerProps\n>(({ className, type = \"button\", ...props }, ref) => (\n <button\n ref={ref}\n type={type}\n className={cn(INLINE_FIELD_ROW, className)}\n {...props}\n />\n));\nInlineFieldTrigger.displayName = \"InlineFieldTrigger\";\n"],"mappings":";;;;;AAMA,IAAa,IAAqB,GAG/B,EAAE,cAAW,UAAO,UAAU,GAAG,KAAS,MAC3C,kBAAC,UAAD;CACO;CACC;CACN,WAAW,EAAG,GAAkB,EAAU;CAC1C,GAAI;CACJ,CAAA,CACF;AACF,EAAmB,cAAc"}
@@ -0,0 +1,14 @@
1
+ export interface InlineMarkdownFieldProps {
2
+ value: string;
3
+ onCommit: (value: string) => void;
4
+ onChange?: (value: string) => void;
5
+ onCancel?: () => void;
6
+ placeholder?: string;
7
+ emptyText?: string;
8
+ disabled?: boolean;
9
+ collapsible?: boolean;
10
+ testId?: string;
11
+ ariaLabel?: string;
12
+ editorClassName?: string;
13
+ }
14
+ export declare const InlineMarkdownField: ({ value, onCommit, onChange, onCancel, placeholder, emptyText, disabled, collapsible, testId, ariaLabel, editorClassName, }: InlineMarkdownFieldProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,70 @@
1
+ import { cn as e } from "../../utilities/class/index.js";
2
+ import { DescriptionMarkdownEditor as t } from "../description_markdown_editor/index.js";
3
+ import { useEditableFields as n } from "../editable_fields/hooks/index.js";
4
+ import { INLINE_FIELD_ROW as r } from "../inline_editable_text/constants/index.js";
5
+ import { useEffect as i, useLayoutEffect as a, useRef as o, useState as s } from "react";
6
+ import { jsx as c } from "react/jsx-runtime";
7
+ //#region src/components/inline_markdown_field/index.tsx
8
+ function l(e) {
9
+ let t = e?.querySelector("[contenteditable=\"true\"]");
10
+ if (!t) return;
11
+ t.focus();
12
+ let n = window.getSelection();
13
+ if (!n) return;
14
+ let r = document.createRange();
15
+ r.selectNodeContents(t), r.collapse(!1), n.removeAllRanges(), n.addRange(r);
16
+ }
17
+ var u = ({ value: u, onCommit: d, onChange: f, onCancel: p, placeholder: m, emptyText: h, disabled: g = !1, collapsible: _ = !1, testId: v, ariaLabel: y, editorClassName: b = "min-h-[120px] text-sm" }) => {
18
+ let { labels: x } = n(), [S, C] = s(u), [w, T] = s(!_), E = o(u), D = o(!1), O = o(null);
19
+ i(() => {
20
+ u !== E.current && (E.current = u, !D.current && C(u));
21
+ }, [u]), a(() => {
22
+ !_ || !w || l(O.current);
23
+ }, [_, w]);
24
+ let k = () => {
25
+ _ && T(!1);
26
+ }, A = () => {
27
+ k(), S !== u && d(S);
28
+ }, j = () => {
29
+ p?.(), C(u), k();
30
+ }, M = (e) => {
31
+ e.currentTarget.contains(e.relatedTarget) || (D.current = !1, A());
32
+ };
33
+ if (_ && !w) {
34
+ let t = u.trim() === "";
35
+ return /* @__PURE__ */ c("button", {
36
+ type: "button",
37
+ "aria-label": y,
38
+ "data-testid": v,
39
+ disabled: g,
40
+ onClick: () => {
41
+ C(u), T(!0);
42
+ },
43
+ className: e(r, "line-clamp-3 whitespace-pre-wrap break-words", "disabled:pointer-events-none disabled:opacity-50", t && "text-muted-foreground"),
44
+ children: t ? h ?? m ?? x.empty : u
45
+ });
46
+ }
47
+ return /* @__PURE__ */ c("div", {
48
+ ref: O,
49
+ "data-testid": v,
50
+ onFocus: () => {
51
+ D.current = !0;
52
+ },
53
+ onBlur: M,
54
+ children: /* @__PURE__ */ c(t, {
55
+ value: S,
56
+ onChange: (e) => {
57
+ C(e), f?.(e);
58
+ },
59
+ onSubmit: A,
60
+ onEscape: j,
61
+ placeholder: m,
62
+ disabled: g,
63
+ editorClassName: b
64
+ })
65
+ });
66
+ };
67
+ //#endregion
68
+ export { u as InlineMarkdownField };
69
+
70
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/inline_markdown_field/index.tsx"],"sourcesContent":["import { useEditableFields } from \"@/components/editable_fields/hooks\";\nimport { DescriptionMarkdownEditor } from \"@/components/description_markdown_editor\";\nimport {\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type FocusEvent,\n} from \"react\";\nimport { cn } from \"@/utilities/class\";\nimport { INLINE_FIELD_ROW } from \"../inline_editable_text/constants\";\n\nexport interface InlineMarkdownFieldProps {\n value: string;\n onCommit: (value: string) => void;\n onChange?: (value: string) => void;\n onCancel?: () => void;\n placeholder?: string;\n emptyText?: string;\n disabled?: boolean;\n collapsible?: boolean;\n testId?: string;\n ariaLabel?: string;\n editorClassName?: string;\n}\n\nfunction focusEditor(container: HTMLElement | null) {\n const editable = container?.querySelector<HTMLElement>(\n '[contenteditable=\"true\"]',\n );\n if (!editable) return;\n\n editable.focus();\n const selection = window.getSelection();\n if (!selection) return;\n\n const range = document.createRange();\n range.selectNodeContents(editable);\n range.collapse(false);\n selection.removeAllRanges();\n selection.addRange(range);\n}\n\nexport const InlineMarkdownField = ({\n value,\n onCommit,\n onChange,\n onCancel,\n placeholder,\n emptyText,\n disabled = false,\n collapsible = false,\n testId,\n ariaLabel,\n editorClassName = \"min-h-[120px] text-sm\",\n}: InlineMarkdownFieldProps) => {\n const { labels } = useEditableFields();\n\n const [draft, setDraft] = useState(value);\n const [isEditing, setIsEditing] = useState(!collapsible);\n const serverRef = useRef(value);\n const isFocusedRef = useRef(false);\n const containerRef = useRef<HTMLDivElement | null>(null);\n\n useEffect(() => {\n if (value === serverRef.current) return;\n serverRef.current = value;\n if (isFocusedRef.current) return;\n setDraft(value);\n }, [value]);\n\n useLayoutEffect(() => {\n if (!collapsible || !isEditing) return;\n focusEditor(containerRef.current);\n }, [collapsible, isEditing]);\n\n const close = () => {\n if (collapsible) setIsEditing(false);\n };\n\n const commit = () => {\n close();\n if (draft === value) return;\n onCommit(draft);\n };\n\n const cancel = () => {\n onCancel?.();\n setDraft(value);\n close();\n };\n\n const handleBlur = (event: FocusEvent<HTMLDivElement>) => {\n if (event.currentTarget.contains(event.relatedTarget)) return;\n isFocusedRef.current = false;\n commit();\n };\n\n if (collapsible && !isEditing) {\n const isEmpty = value.trim() === \"\";\n\n return (\n <button\n type=\"button\"\n aria-label={ariaLabel}\n data-testid={testId}\n disabled={disabled}\n onClick={() => {\n setDraft(value);\n setIsEditing(true);\n }}\n className={cn(\n INLINE_FIELD_ROW,\n \"line-clamp-3 whitespace-pre-wrap break-words\",\n \"disabled:pointer-events-none disabled:opacity-50\",\n isEmpty && \"text-muted-foreground\",\n )}\n >\n {isEmpty ? (emptyText ?? placeholder ?? labels.empty) : value}\n </button>\n );\n }\n\n return (\n <div\n ref={containerRef}\n data-testid={testId}\n onFocus={() => {\n isFocusedRef.current = true;\n }}\n onBlur={handleBlur}\n >\n <DescriptionMarkdownEditor\n value={draft}\n onChange={(markdown) => {\n setDraft(markdown);\n onChange?.(markdown);\n }}\n onSubmit={commit}\n onEscape={cancel}\n placeholder={placeholder}\n disabled={disabled}\n editorClassName={editorClassName}\n />\n </div>\n );\n};\n"],"mappings":";;;;;;;AA0BA,SAAS,EAAY,GAA+B;CAClD,IAAM,IAAW,GAAW,cAC1B,6BACD;AACD,KAAI,CAAC,EAAU;AAEf,GAAS,OAAO;CAChB,IAAM,IAAY,OAAO,cAAc;AACvC,KAAI,CAAC,EAAW;CAEhB,IAAM,IAAQ,SAAS,aAAa;AAIpC,CAHA,EAAM,mBAAmB,EAAS,EAClC,EAAM,SAAS,GAAM,EACrB,EAAU,iBAAiB,EAC3B,EAAU,SAAS,EAAM;;AAG3B,IAAa,KAAuB,EAClC,UACA,aACA,aACA,aACA,gBACA,cACA,cAAW,IACX,iBAAc,IACd,WACA,cACA,qBAAkB,8BACY;CAC9B,IAAM,EAAE,cAAW,GAAmB,EAEhC,CAAC,GAAO,KAAY,EAAS,EAAM,EACnC,CAAC,GAAW,KAAgB,EAAS,CAAC,EAAY,EAClD,IAAY,EAAO,EAAM,EACzB,IAAe,EAAO,GAAM,EAC5B,IAAe,EAA8B,KAAK;AASxD,CAPA,QAAgB;AACV,QAAU,EAAU,YACxB,EAAU,UAAU,GAChB,GAAa,WACjB,EAAS,EAAM;IACd,CAAC,EAAM,CAAC,EAEX,QAAsB;AAChB,GAAC,KAAe,CAAC,KACrB,EAAY,EAAa,QAAQ;IAChC,CAAC,GAAa,EAAU,CAAC;CAE5B,IAAM,UAAc;AAClB,EAAI,KAAa,EAAa,GAAM;IAGhC,UAAe;AACnB,KAAO,EACH,MAAU,KACd,EAAS,EAAM;IAGX,UAAe;AAGnB,EAFA,KAAY,EACZ,EAAS,EAAM,EACf,GAAO;IAGH,KAAc,MAAsC;AACpD,IAAM,cAAc,SAAS,EAAM,cAAc,KACrD,EAAa,UAAU,IACvB,GAAQ;;AAGV,KAAI,KAAe,CAAC,GAAW;EAC7B,IAAM,IAAU,EAAM,MAAM,KAAK;AAEjC,SACE,kBAAC,UAAD;GACE,MAAK;GACL,cAAY;GACZ,eAAa;GACH;GACV,eAAe;AAEb,IADA,EAAS,EAAM,EACf,EAAa,GAAK;;GAEpB,WAAW,EACT,GACA,gDACA,oDACA,KAAW,wBACZ;aAEA,IAAW,KAAa,KAAe,EAAO,QAAS;GACjD,CAAA;;AAIb,QACE,kBAAC,OAAD;EACE,KAAK;EACL,eAAa;EACb,eAAe;AACb,KAAa,UAAU;;EAEzB,QAAQ;YAER,kBAAC,GAAD;GACE,OAAO;GACP,WAAW,MAAa;AAEtB,IADA,EAAS,EAAS,EAClB,IAAW,EAAS;;GAEtB,UAAU;GACV,UAAU;GACG;GACH;GACO;GACjB,CAAA;EACE,CAAA"}
@@ -0,0 +1,19 @@
1
+ import { LucideIcon } from 'lucide-react';
2
+ import { ReactNode } from 'react';
3
+ export interface InlineOption {
4
+ value: string;
5
+ label: string;
6
+ icon?: LucideIcon;
7
+ }
8
+ export interface InlineOptionFieldProps {
9
+ value: string | null;
10
+ options: InlineOption[];
11
+ onChange: (next: string | null) => void;
12
+ ariaLabel: string;
13
+ emptyText: string;
14
+ testId?: string;
15
+ clearLabel?: string;
16
+ disabled?: boolean;
17
+ renderOption?: (option: InlineOption) => ReactNode;
18
+ }
19
+ export declare const InlineOptionField: ({ value, options, onChange, ariaLabel, emptyText, testId, clearLabel, disabled, renderOption, }: InlineOptionFieldProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,56 @@
1
+ import { cn as e } from "../../utilities/class/index.js";
2
+ import { Popover as t, PopoverContent as n, PopoverTrigger as r } from "../popover/index.js";
3
+ import { INLINE_FIELD_ROW as i } from "../inline_editable_text/constants/index.js";
4
+ import { useState as a } from "react";
5
+ import { Fragment as o, jsx as s, jsxs as c } from "react/jsx-runtime";
6
+ //#region src/components/inline_option_field/index.tsx
7
+ var l = ({ value: l, options: u, onChange: d, ariaLabel: f, emptyText: p, testId: m, clearLabel: h, disabled: g, renderOption: _ }) => {
8
+ let [v, y] = a(!1), b = u.find((e) => e.value === l) ?? null, x = b?.icon, S = (e) => {
9
+ y(!1), e !== l && d(e);
10
+ };
11
+ return /* @__PURE__ */ c(t, {
12
+ open: v,
13
+ onOpenChange: y,
14
+ children: [/* @__PURE__ */ s(r, {
15
+ asChild: !0,
16
+ children: /* @__PURE__ */ s("button", {
17
+ type: "button",
18
+ title: f,
19
+ "aria-label": f,
20
+ "data-testid": m,
21
+ disabled: g,
22
+ className: e(i, "w-auto px-2 disabled:pointer-events-none disabled:opacity-50", !b && "text-muted-foreground"),
23
+ children: b ? _?.(b) ?? /* @__PURE__ */ c("span", {
24
+ className: "flex items-center gap-2",
25
+ children: [x ? /* @__PURE__ */ s(x, { className: "size-4 shrink-0" }) : null, b.label]
26
+ }) : p
27
+ })
28
+ }), /* @__PURE__ */ s(n, {
29
+ align: "start",
30
+ className: "w-auto min-w-52 p-1",
31
+ children: /* @__PURE__ */ c("div", {
32
+ className: "flex flex-col",
33
+ children: [u.map((t) => {
34
+ let n = t.icon;
35
+ return /* @__PURE__ */ s("button", {
36
+ type: "button",
37
+ "data-testid": m ? `${m}-option-${t.value}` : void 0,
38
+ onClick: () => S(t.value),
39
+ className: e("flex items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm hover:bg-accent", t.value === l && "bg-accent"),
40
+ children: _?.(t) ?? /* @__PURE__ */ c(o, { children: [n ? /* @__PURE__ */ s(n, { className: "size-4 shrink-0" }) : null, t.label] })
41
+ }, t.value);
42
+ }), h ? /* @__PURE__ */ s("button", {
43
+ type: "button",
44
+ "data-testid": m ? `${m}-clear` : void 0,
45
+ onClick: () => S(null),
46
+ className: "flex items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm text-muted-foreground hover:bg-accent",
47
+ children: h
48
+ }) : null]
49
+ })
50
+ })]
51
+ });
52
+ };
53
+ //#endregion
54
+ export { l as InlineOptionField };
55
+
56
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/inline_option_field/index.tsx"],"sourcesContent":["import { Popover, PopoverContent, PopoverTrigger } from \"@/components/popover\";\nimport type { LucideIcon } from \"lucide-react\";\nimport { useState, type ReactNode } from \"react\";\nimport { cn } from \"@/utilities/class\";\nimport { INLINE_FIELD_ROW } from \"../inline_editable_text/constants\";\n\nexport interface InlineOption {\n value: string;\n label: string;\n icon?: LucideIcon;\n}\n\nexport interface InlineOptionFieldProps {\n value: string | null;\n options: InlineOption[];\n onChange: (next: string | null) => void;\n ariaLabel: string;\n emptyText: string;\n testId?: string;\n clearLabel?: string;\n disabled?: boolean;\n renderOption?: (option: InlineOption) => ReactNode;\n}\n\nexport const InlineOptionField = ({\n value,\n options,\n onChange,\n ariaLabel,\n emptyText,\n testId,\n clearLabel,\n disabled,\n renderOption,\n}: InlineOptionFieldProps) => {\n const [open, setOpen] = useState(false);\n const selected = options.find((option) => option.value === value) ?? null;\n const SelectedIcon = selected?.icon;\n\n const choose = (next: string | null) => {\n setOpen(false);\n if (next === value) return;\n onChange(next);\n };\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n title={ariaLabel}\n aria-label={ariaLabel}\n data-testid={testId}\n disabled={disabled}\n className={cn(\n INLINE_FIELD_ROW,\n \"w-auto px-2 disabled:pointer-events-none disabled:opacity-50\",\n !selected && \"text-muted-foreground\",\n )}\n >\n {selected\n ? (renderOption?.(selected) ?? (\n <span className=\"flex items-center gap-2\">\n {SelectedIcon ? (\n <SelectedIcon className=\"size-4 shrink-0\" />\n ) : null}\n {selected.label}\n </span>\n ))\n : emptyText}\n </button>\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"w-auto min-w-52 p-1\">\n <div className=\"flex flex-col\">\n {options.map((option) => {\n const Icon = option.icon;\n return (\n <button\n key={option.value}\n type=\"button\"\n data-testid={\n testId ? `${testId}-option-${option.value}` : undefined\n }\n onClick={() => choose(option.value)}\n className={cn(\n \"flex items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm hover:bg-accent\",\n option.value === value && \"bg-accent\",\n )}\n >\n {renderOption?.(option) ?? (\n <>\n {Icon ? <Icon className=\"size-4 shrink-0\" /> : null}\n {option.label}\n </>\n )}\n </button>\n );\n })}\n {clearLabel ? (\n <button\n type=\"button\"\n data-testid={testId ? `${testId}-clear` : undefined}\n onClick={() => choose(null)}\n className=\"flex items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm text-muted-foreground hover:bg-accent\"\n >\n {clearLabel}\n </button>\n ) : null}\n </div>\n </PopoverContent>\n </Popover>\n );\n};\n"],"mappings":";;;;;;AAwBA,IAAa,KAAqB,EAChC,UACA,YACA,aACA,cACA,cACA,WACA,eACA,aACA,sBAC4B;CAC5B,IAAM,CAAC,GAAM,KAAW,EAAS,GAAM,EACjC,IAAW,EAAQ,MAAM,MAAW,EAAO,UAAU,EAAM,IAAI,MAC/D,IAAe,GAAU,MAEzB,KAAU,MAAwB;AACtC,IAAQ,GAAM,EACV,MAAS,KACb,EAAS,EAAK;;AAGhB,QACE,kBAAC,GAAD;EAAe;EAAM,cAAc;YAAnC,CACE,kBAAC,GAAD;GAAgB,SAAA;aACd,kBAAC,UAAD;IACE,MAAK;IACL,OAAO;IACP,cAAY;IACZ,eAAa;IACH;IACV,WAAW,EACT,GACA,gEACA,CAAC,KAAY,wBACd;cAEA,IACI,IAAe,EAAS,IACvB,kBAAC,QAAD;KAAM,WAAU;eAAhB,CACG,IACC,kBAAC,GAAD,EAAc,WAAU,mBAAoB,CAAA,GAC1C,MACH,EAAS,MACL;SAET;IACG,CAAA;GACM,CAAA,EACjB,kBAAC,GAAD;GAAgB,OAAM;GAAQ,WAAU;aACtC,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,EAAQ,KAAK,MAAW;KACvB,IAAM,IAAO,EAAO;AACpB,YACE,kBAAC,UAAD;MAEE,MAAK;MACL,eACE,IAAS,GAAG,EAAO,UAAU,EAAO,UAAU,KAAA;MAEhD,eAAe,EAAO,EAAO,MAAM;MACnC,WAAW,EACT,oFACA,EAAO,UAAU,KAAS,YAC3B;gBAEA,IAAe,EAAO,IACrB,kBAAA,GAAA,EAAA,UAAA,CACG,IAAO,kBAAC,GAAD,EAAM,WAAU,mBAAoB,CAAA,GAAG,MAC9C,EAAO,MACP,EAAA,CAAA;MAEE,EAjBF,EAAO,MAiBL;MAEX,EACD,IACC,kBAAC,UAAD;KACE,MAAK;KACL,eAAa,IAAS,GAAG,EAAO,UAAU,KAAA;KAC1C,eAAe,EAAO,KAAK;KAC3B,WAAU;eAET;KACM,CAAA,GACP,KACA;;GACS,CAAA,CACT"}
@@ -0,0 +1,12 @@
1
+ export interface OfflineBannerLabels {
2
+ offline: string;
3
+ reconnected: string;
4
+ close: string;
5
+ }
6
+ export interface OfflineBannerProps {
7
+ isOnline: boolean;
8
+ labels?: Partial<OfflineBannerLabels>;
9
+ className?: string;
10
+ reconnectedDurationMs?: number;
11
+ }
12
+ export declare function OfflineBanner({ isOnline, labels, className, reconnectedDurationMs, }: OfflineBannerProps): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,76 @@
1
+ import { cn as e } from "../../utilities/class/index.js";
2
+ /* empty css */
3
+ import { useEffect as t, useState as n } from "react";
4
+ import { jsx as r, jsxs as i } from "react/jsx-runtime";
5
+ import { Wifi as a, WifiOff as o, X as s } from "lucide-react";
6
+ //#region src/components/offline_banner/index.tsx
7
+ var c = 300;
8
+ function l({ isOnline: l, labels: u, className: d, reconnectedDurationMs: f = 3e3 }) {
9
+ let p = {
10
+ offline: "No tienes internet",
11
+ reconnected: "Vuelves a estar conectado",
12
+ close: "Cerrar",
13
+ ...u
14
+ }, [m, h] = n(() => ({
15
+ isOnline: l,
16
+ status: l ? null : "offline",
17
+ lastStatus: "offline",
18
+ mounted: !l
19
+ }));
20
+ if (m.isOnline !== l) {
21
+ let e = l ? "reconnected" : "offline";
22
+ h({
23
+ isOnline: l,
24
+ status: e,
25
+ lastStatus: e,
26
+ mounted: !0
27
+ });
28
+ }
29
+ let { status: g, lastStatus: _, mounted: v } = m;
30
+ if (t(() => {
31
+ if (g !== "reconnected") return;
32
+ let e = setTimeout(() => h((e) => ({
33
+ ...e,
34
+ status: null
35
+ })), f);
36
+ return () => clearTimeout(e);
37
+ }, [g, f]), t(() => {
38
+ if (g !== null || !v) return;
39
+ let e = setTimeout(() => h((e) => ({
40
+ ...e,
41
+ mounted: !1
42
+ })), c);
43
+ return () => clearTimeout(e);
44
+ }, [g, v]), !v) return null;
45
+ let y = _ === "offline", b = y ? o : a;
46
+ return /* @__PURE__ */ i("div", {
47
+ role: "status",
48
+ "aria-live": "polite",
49
+ "data-state": g === null ? "closed" : "open",
50
+ className: e("arxatec-offline-banner fixed inset-x-0 top-0 z-[100]", "flex items-center justify-center gap-2", "py-2 pr-10 pl-4 text-center text-sm font-medium", "transition-all duration-300 ease-out border-border border-b", y ? "bg-sidebar text-foreground" : "bg-primary text-background", g === null ? "-translate-y-full opacity-0" : "translate-y-0 opacity-100", d),
51
+ children: [
52
+ /* @__PURE__ */ r(b, {
53
+ className: "size-4 shrink-0",
54
+ "aria-hidden": !0
55
+ }),
56
+ /* @__PURE__ */ r("span", { children: y ? p.offline : p.reconnected }),
57
+ y ? /* @__PURE__ */ r("button", {
58
+ type: "button",
59
+ onClick: () => h((e) => ({
60
+ ...e,
61
+ status: null
62
+ })),
63
+ "aria-label": p.close,
64
+ className: "absolute top-1/2 right-3 -translate-y-1/2 rounded-md p-1 opacity-80 transition-opacity hover:opacity-100",
65
+ children: /* @__PURE__ */ r(s, {
66
+ className: "size-4",
67
+ "aria-hidden": !0
68
+ })
69
+ }) : null
70
+ ]
71
+ });
72
+ }
73
+ //#endregion
74
+ export { l as OfflineBanner };
75
+
76
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/offline_banner/index.tsx"],"sourcesContent":["import \"./styles.css\";\nimport { Wifi, WifiOff, X } from \"lucide-react\";\nimport { useEffect, useState } from \"react\";\nimport { cn } from \"@/utilities/class\";\n\nconst TRANSITION_MS = 300;\ntype Status = \"offline\" | \"reconnected\";\n\nexport interface OfflineBannerLabels {\n offline: string;\n reconnected: string;\n close: string;\n}\n\nexport interface OfflineBannerProps {\n isOnline: boolean;\n labels?: Partial<OfflineBannerLabels>;\n className?: string;\n reconnectedDurationMs?: number;\n}\n\ninterface ConnectionState {\n isOnline: boolean;\n status: Status | null;\n lastStatus: Status;\n mounted: boolean;\n}\n\nexport function OfflineBanner({\n isOnline,\n labels,\n className,\n reconnectedDurationMs = 3000,\n}: OfflineBannerProps) {\n const copy = {\n offline: \"No tienes internet\",\n reconnected: \"Vuelves a estar conectado\",\n close: \"Cerrar\",\n ...labels,\n };\n const [connection, setConnection] = useState<ConnectionState>(() => ({\n isOnline,\n status: isOnline ? null : \"offline\",\n lastStatus: \"offline\",\n mounted: !isOnline,\n }));\n\n if (connection.isOnline !== isOnline) {\n const status = isOnline ? \"reconnected\" : \"offline\";\n setConnection({ isOnline, status, lastStatus: status, mounted: true });\n }\n\n const { status, lastStatus, mounted } = connection;\n\n useEffect(() => {\n if (status !== \"reconnected\") return;\n const timer = setTimeout(\n () => setConnection((current) => ({ ...current, status: null })),\n reconnectedDurationMs,\n );\n return () => clearTimeout(timer);\n }, [status, reconnectedDurationMs]);\n\n useEffect(() => {\n if (status !== null || !mounted) return;\n const timer = setTimeout(\n () => setConnection((current) => ({ ...current, mounted: false })),\n TRANSITION_MS,\n );\n return () => clearTimeout(timer);\n }, [status, mounted]);\n\n if (!mounted) return null;\n const isOffline = lastStatus === \"offline\";\n const Icon = isOffline ? WifiOff : Wifi;\n\n return (\n <div\n role=\"status\"\n aria-live=\"polite\"\n data-state={status === null ? \"closed\" : \"open\"}\n className={cn(\n \"arxatec-offline-banner fixed inset-x-0 top-0 z-[100]\",\n \"flex items-center justify-center gap-2\",\n \"py-2 pr-10 pl-4 text-center text-sm font-medium\",\n \"transition-all duration-300 ease-out border-border border-b\",\n isOffline ? \"bg-sidebar text-foreground\" : \"bg-primary text-background\",\n status !== null\n ? \"translate-y-0 opacity-100\"\n : \"-translate-y-full opacity-0\",\n className,\n )}\n >\n <Icon className=\"size-4 shrink-0\" aria-hidden />\n <span>{isOffline ? copy.offline : copy.reconnected}</span>\n {isOffline ? (\n <button\n type=\"button\"\n onClick={() =>\n setConnection((current) => ({ ...current, status: null }))\n }\n aria-label={copy.close}\n className=\"absolute top-1/2 right-3 -translate-y-1/2 rounded-md p-1 opacity-80 transition-opacity hover:opacity-100\"\n >\n <X className=\"size-4\" aria-hidden />\n </button>\n ) : null}\n </div>\n );\n}\n"],"mappings":";;;;;;AAKA,IAAM,IAAgB;AAuBtB,SAAgB,EAAc,EAC5B,aACA,WACA,cACA,2BAAwB,OACH;CACrB,IAAM,IAAO;EACX,SAAS;EACT,aAAa;EACb,OAAO;EACP,GAAG;EACJ,EACK,CAAC,GAAY,KAAiB,SAAiC;EACnE;EACA,QAAQ,IAAW,OAAO;EAC1B,YAAY;EACZ,SAAS,CAAC;EACX,EAAE;AAEH,KAAI,EAAW,aAAa,GAAU;EACpC,IAAM,IAAS,IAAW,gBAAgB;AAC1C,IAAc;GAAE;GAAU;GAAQ,YAAY;GAAQ,SAAS;GAAM,CAAC;;CAGxE,IAAM,EAAE,WAAQ,eAAY,eAAY;AAoBxC,KAlBA,QAAgB;AACd,MAAI,MAAW,cAAe;EAC9B,IAAM,IAAQ,iBACN,GAAe,OAAa;GAAE,GAAG;GAAS,QAAQ;GAAM,EAAE,EAChE,EACD;AACD,eAAa,aAAa,EAAM;IAC/B,CAAC,GAAQ,EAAsB,CAAC,EAEnC,QAAgB;AACd,MAAI,MAAW,QAAQ,CAAC,EAAS;EACjC,IAAM,IAAQ,iBACN,GAAe,OAAa;GAAE,GAAG;GAAS,SAAS;GAAO,EAAE,EAClE,EACD;AACD,eAAa,aAAa,EAAM;IAC/B,CAAC,GAAQ,EAAQ,CAAC,EAEjB,CAAC,EAAS,QAAO;CACrB,IAAM,IAAY,MAAe,WAC3B,IAAO,IAAY,IAAU;AAEnC,QACE,kBAAC,OAAD;EACE,MAAK;EACL,aAAU;EACV,cAAY,MAAW,OAAO,WAAW;EACzC,WAAW,EACT,wDACA,0CACA,mDACA,+DACA,IAAY,+BAA+B,8BAC3C,MAAW,OAEP,gCADA,6BAEJ,EACD;YAdH;GAgBE,kBAAC,GAAD;IAAM,WAAU;IAAkB,eAAA;IAAc,CAAA;GAChD,kBAAC,QAAD,EAAA,UAAO,IAAY,EAAK,UAAU,EAAK,aAAmB,CAAA;GACzD,IACC,kBAAC,UAAD;IACE,MAAK;IACL,eACE,GAAe,OAAa;KAAE,GAAG;KAAS,QAAQ;KAAM,EAAE;IAE5D,cAAY,EAAK;IACjB,WAAU;cAEV,kBAAC,GAAD;KAAG,WAAU;KAAS,eAAA;KAAc,CAAA;IAC7B,CAAA,GACP;GACA"}
@@ -0,0 +1,10 @@
1
+ import { LucideIcon } from 'lucide-react';
2
+ import { ReactNode } from 'react';
3
+ export interface PropertyRowProps {
4
+ icon: LucideIcon;
5
+ label: string;
6
+ stacked?: boolean;
7
+ status?: ReactNode;
8
+ children: ReactNode;
9
+ }
10
+ export declare const PropertyRow: ({ icon: Icon, label, stacked, status, children, }: PropertyRowProps) => import("react/jsx-runtime").JSX.Element;