RemarkTemplateField.tsx 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. import { useRef } from 'react';
  2. import { Button, Input, Popover, Tooltip } from 'antd';
  3. import type { InputRef } from 'antd';
  4. import { CodeOutlined } from '@ant-design/icons';
  5. import { useTranslation } from 'react-i18next';
  6. import { hasRemarkTokens, previewRemark, wrapToken } from '@/lib/remark/remarkVariables';
  7. import RemarkVarPicker from './RemarkVarPicker';
  8. interface RemarkTemplateFieldProps {
  9. // Injected by antd Form.Item:
  10. value?: string;
  11. onChange?: (value: string) => void;
  12. maxLength?: number;
  13. placeholder?: string;
  14. }
  15. /**
  16. * RemarkTemplateField is a text input augmented with a {{VAR}} template picker
  17. * (insert-at-caret) and a live, sample-based preview of the expanded result.
  18. * Used for the global subscription Remark Template.
  19. */
  20. export default function RemarkTemplateField({ value = '', onChange, maxLength, placeholder }: RemarkTemplateFieldProps) {
  21. const { t } = useTranslation();
  22. const inputRef = useRef<InputRef>(null);
  23. function insertToken(token: string) {
  24. const el = inputRef.current?.input;
  25. const start = el?.selectionStart ?? value.length;
  26. const end = el?.selectionEnd ?? value.length;
  27. const insert = wrapToken(token);
  28. const next = value.slice(0, start) + insert + value.slice(end);
  29. onChange?.(maxLength ? next.slice(0, maxLength) : next);
  30. const caret = start + insert.length;
  31. // The controlled value updates next render; restore the caret after it.
  32. requestAnimationFrame(() => {
  33. el?.focus();
  34. el?.setSelectionRange(caret, caret);
  35. });
  36. }
  37. return (
  38. <div>
  39. <Input
  40. ref={inputRef}
  41. value={value}
  42. maxLength={maxLength}
  43. placeholder={placeholder}
  44. onChange={(e) => onChange?.(e.target.value)}
  45. addonAfter={
  46. <Popover
  47. content={<RemarkVarPicker onPick={insertToken} />}
  48. trigger="click"
  49. placement="bottomRight"
  50. title={t('pages.hosts.remarkVars.title')}
  51. >
  52. <Tooltip title={t('pages.hosts.remarkVars.title')}>
  53. <Button type="text" size="small" icon={<CodeOutlined />} style={{ margin: '0 -7px' }} />
  54. </Tooltip>
  55. </Popover>
  56. }
  57. />
  58. {hasRemarkTokens(value) && (
  59. <div style={{ fontSize: 12, marginTop: 4, opacity: 0.7 }}>
  60. {t('pages.hosts.remarkVars.preview')}:{' '}
  61. <span style={{ fontFamily: 'monospace' }}>{previewRemark(value) || '—'}</span>
  62. </div>
  63. )}
  64. </div>
  65. );
  66. }