3x-ui/frontend/src/components
MHSanaei 22e88ec4eb
refactor(frontend): port nodes to react+ts
Step 4 of the planned vue->react migration. The nodes entry brings in
the largest shared-infrastructure batch so far — every authenticated
react page from here on can lean on these.

New shared pieces (live alongside their .vue counterparts during
coexistence):

* hooks/useMediaQuery.ts — useState + resize listener
* hooks/useWebSocket.ts — wraps WebSocketClient, subscribes on mount
  and unsubscribes on unmount. The underlying client is a single
  module-level instance so multiple components on the same page
  share one socket.
* hooks/useNodes.ts — node list state + CRUD + probe/test, including
  the totals memo (online/offline/avgLatency) used by the summary card.
  applyNodesEvent is the entry point for the heartbeat-pushed list.
* components/CustomStatistic.tsx — thin Statistic wrapper, prefix +
  suffix slots become props.
* components/Sparkline.tsx — the SVG line chart with measured-width
  axis scaling, gradient fill, tooltip overlay, and per-instance
  gradient id from React.useId. ResizeObserver lifecycle is in
  useEffect; the math is unchanged.

Pages:

* NodesPage — wires hooks + WebSocket together, renders summary card
  + NodeList, hosts the form modal. Uses Modal.useModal() for the
  delete confirm so the dialog inherits ConfigProvider theming.
* NodeList — desktop renders a Table with expandable history rows;
  mobile flips to a vertical card list whose actions live in a
  bottom-right Dropdown. The IP-blur eye toggle persists across both.
* NodeFormModal — controlled form (useState object, single setForm
  per change). The reset-on-open effect computes the next state
  once and applies it with eslint-disable to satisfy the new
  react-hooks/set-state-in-effect rule on a legitimate pattern.
* NodeHistoryPanel — polls /panel/api/nodes/history/{id}/{metric}/
  {bucket} every 15s, renders cpu+mem sparklines side-by-side.
2026-05-21 21:34:46 +02:00
..
AppSidebar.css refactor(frontend): port api-docs to react+ts 2026-05-21 21:26:28 +02:00
AppSidebar.tsx refactor(frontend): port api-docs to react+ts 2026-05-21 21:26:28 +02:00
AppSidebar.vue Feat/multi inbound clients (#4469) 2026-05-19 12:20:24 +02:00
CustomStatistic.css refactor(frontend): port nodes to react+ts 2026-05-21 21:34:46 +02:00
CustomStatistic.tsx refactor(frontend): port nodes to react+ts 2026-05-21 21:34:46 +02:00
CustomStatistic.vue feat(xray/dns): align DNS settings with Xray docs + UI polish 2026-05-10 17:03:11 +02:00
DateTimePicker.vue feat(frontend): refresh dark theme + redesign login page 2026-05-11 01:10:05 +02:00
FinalMaskForm.vue feat(xray/dns): align DNS settings with Xray docs + UI polish 2026-05-10 17:03:11 +02:00
InfinityIcon.vue feat(xray/dns): align DNS settings with Xray docs + UI polish 2026-05-10 17:03:11 +02:00
JsonEditor.vue feat(json): swap raw textareas for a CodeMirror 6 JsonEditor 2026-05-14 00:02:59 +02:00
PromptModal.vue feat(xray/dns): align DNS settings with Xray docs + UI polish 2026-05-10 17:03:11 +02:00
SettingListItem.vue feat(xray/dns): align DNS settings with Xray docs + UI polish 2026-05-10 17:03:11 +02:00
Sparkline.css refactor(frontend): port nodes to react+ts 2026-05-21 21:34:46 +02:00
Sparkline.tsx refactor(frontend): port nodes to react+ts 2026-05-21 21:34:46 +02:00
Sparkline.vue fix(graphs): increase y-axis paddingLeft from 32 to 56 to prevent clipped labels (#4309) 2026-05-13 01:47:54 +02:00
TableSortable.vue feat(xray/dns): align DNS settings with Xray docs + UI polish 2026-05-10 17:03:11 +02:00
TextModal.vue feat(xray/dns): align DNS settings with Xray docs + UI polish 2026-05-10 17:03:11 +02:00