tycho-components 0.25.2 → 0.25.5

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.
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { AsyncSelectField, CheckboxField, DatePickerField, SelectField, SelectMultipleField, SwitchField, TextField, } from 'tycho-storybook';
3
+ import FormColorField from './FormColorField';
3
4
  import './style.scss';
4
5
  const convertList = (values, labelAttr, valueAttr) => {
5
6
  if (!values)
@@ -58,6 +59,9 @@ export default function AppForm({ fields, form, prefix, onChangeFunctions = {},
58
59
  if (field.type === 'textarea') {
59
60
  return (_jsx(TextField, { attr: name, label: field.name, createdForm: form, disabled: field.disabled, required: field.required, onChange: (value) => handleChange?.(value), multiline: true, rows: field.rows, height: "100%" }, name));
60
61
  }
62
+ if (field.type === 'color') {
63
+ return (_jsx(FormColorField, { attr: name, label: field.name, form: form, disabled: field.disabled, tooltipText: field.tooltipText }, name));
64
+ }
61
65
  const handleBlur = getOnBlurFn(field.attr);
62
66
  return (_jsx(TextField, { attr: name, label: field.size === 'small' ? undefined : field.name, placeholder: field.size === 'small' ? field.name : undefined, createdForm: form, mask: field.mask, blurMask: field.blurMask, disabled: field.disabled, required: field.required, onChange: (value) => handleChange?.(value), height: field.size === 'small' ? '32px' : undefined, onBlur: handleBlur }, name));
63
67
  }) }));
@@ -0,0 +1,11 @@
1
+ import { UseFormReturn } from 'react-hook-form';
2
+ import '../AppColorpicker/style.scss';
3
+ type Props = {
4
+ attr: string;
5
+ label: string;
6
+ form: UseFormReturn<any, any, any>;
7
+ disabled?: boolean;
8
+ tooltipText?: string;
9
+ };
10
+ export default function FormColorField({ attr, label, form, disabled, tooltipText, }: Props): import("react/jsx-runtime").JSX.Element;
11
+ export {};
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { HexColorPicker } from 'react-colorful';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { Button, Icon, Tooltip } from 'tycho-storybook';
6
+ import '../AppColorpicker/style.scss';
7
+ const defaultColor = '#FFFFFF';
8
+ export default function FormColorField({ attr, label, form, disabled, tooltipText, }) {
9
+ const { t } = useTranslation('common');
10
+ const formValue = form.watch(attr);
11
+ const [openColor, setOpenColor] = useState(false);
12
+ const [color, setColor] = useState(defaultColor);
13
+ useEffect(() => {
14
+ setColor(formValue || defaultColor);
15
+ }, [formValue]);
16
+ const handleConfirm = () => {
17
+ form.setValue(attr, color, { shouldDirty: true });
18
+ setOpenColor(false);
19
+ };
20
+ return (_jsxs("div", { className: "form-color-field", children: [_jsxs("div", { className: "form-color-field-label", children: [_jsx("span", { children: label }), tooltipText && (_jsx(Tooltip, { title: tooltipText, children: _jsx("span", { children: _jsx(Icon, { name: "help", className: "info", size: "x-small" }) }) }))] }), _jsxs("div", { className: "color-picker-container", children: [_jsx("div", { className: "swatch", style: { backgroundColor: color }, onClick: () => !disabled && setOpenColor(!openColor), onKeyDown: () => !disabled && setOpenColor(!openColor), role: "link", "aria-label": "open color picker", tabIndex: disabled ? -1 : 0 }), openColor && !disabled && (_jsxs(_Fragment, { children: [_jsx(HexColorPicker, { color: color, onChange: setColor }), _jsxs("div", { className: "buttons", children: [_jsx(Button, { text: t('button.confirm'), onClick: handleConfirm, size: "x-small" }), _jsx(Button, { text: t('button.cancel'), onClick: () => setOpenColor(false), color: "danger", size: "x-small" })] })] }))] })] }));
21
+ }
@@ -40,3 +40,21 @@
40
40
  gap: 16px;
41
41
  }
42
42
  }
43
+
44
+ .form-color-field {
45
+ display: flex;
46
+ flex-direction: column;
47
+ gap: var(--spacing-100);
48
+
49
+ .form-color-field-label {
50
+ display: flex;
51
+ align-items: center;
52
+ gap: var(--spacing-50);
53
+ @include label-medium-2;
54
+ color: var(--text-primary);
55
+
56
+ .info {
57
+ color: var(--icon-primary);
58
+ }
59
+ }
60
+ }
@@ -1,7 +1,6 @@
1
- import { AppEditableField } from '../../common/AppEditable/AppEditableField';
2
1
  import Participant, { ParticipantCreateRequest } from './types/Participant';
3
2
  declare function add(uid: string, request: ParticipantCreateRequest): Promise<import("axios").AxiosResponse<Participant, any, {}>>;
4
- declare function update(field: AppEditableField): Promise<import("axios").AxiosResponse<any, any, {}>>;
3
+ declare function update(uid: string, code: string, participant: Participant): Promise<import("axios").AxiosResponse<any, any, {}>>;
5
4
  declare function remove(document: string, code: string, transfer?: string): Promise<import("axios").AxiosResponse<any, any, {}>>;
6
5
  declare const ParticipantService: {
7
6
  add: typeof add;
@@ -2,8 +2,9 @@ import api, { platformApi } from '../../configs/api';
2
2
  function add(uid, request) {
3
3
  return api.post(`${platformApi.catalog}/participant/${uid}`, request);
4
4
  }
5
- function update(field) {
6
- return api.patch(`${platformApi.catalog}/participant/update`, field);
5
+ function update(uid, code, participant) {
6
+ const request = { uid, code, participant };
7
+ return api.patch(`${platformApi.catalog}/participant/update`, request);
7
8
  }
8
9
  function remove(document, code, transfer) {
9
10
  const request = {
@@ -1,34 +1,99 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { MenuItem, Select } from '@mui/material';
3
- import { useEffect, useState } from 'react';
3
+ import { useEffect, useMemo, useState } from 'react';
4
+ import { useForm } from 'react-hook-form';
4
5
  import { useTranslation } from 'react-i18next';
5
6
  import { Button } from 'tycho-storybook';
6
- import AppEditable from '../../common/AppEditable';
7
+ import AppForm from '../../common/AppForm/AppForm';
7
8
  import { useMessageUtils } from '../../configs/useMessageUtils';
8
9
  import ParticipantCreate from './ParticipantCreate';
9
10
  import ParticipantRemove from './ParticipantRemove';
10
11
  import './style.scss';
11
- import { CHAT_FIELDS, PARTICIPANT_FIELDS, } from './types/Participant';
12
12
  import ParticipantService from './ParticipantService';
13
+ const PARTICIPANT_FIELD_DEFS = [
14
+ { attr: 'order', type: 'text', required: true },
15
+ { attr: 'code', type: 'text', required: true },
16
+ { attr: 'name', type: 'text', required: false },
17
+ { attr: 'separator', type: 'text', required: false },
18
+ { attr: 'color', type: 'color', required: false, tooltip: true },
19
+ ];
20
+ const CHAT_FIELD_DEFS = [
21
+ { attr: 'language', type: 'text', required: false },
22
+ { attr: 'age', type: 'text', required: false },
23
+ { attr: 'sex', type: 'text', required: false },
24
+ { attr: 'group', type: 'text', required: false },
25
+ { attr: 'ses', type: 'text', required: false },
26
+ { attr: 'role', type: 'text', required: false },
27
+ { attr: 'education', type: 'text', required: false },
28
+ { attr: 'custom', type: 'text', required: false },
29
+ ];
30
+ const toCoreFormValues = (participant) => ({
31
+ order: participant.order ?? '',
32
+ code: participant.code ?? '',
33
+ name: participant.name ?? '',
34
+ separator: participant.separator ?? '',
35
+ color: participant.color ?? '',
36
+ });
37
+ const toChatFormValues = (participant) => ({
38
+ language: participant.language ?? '',
39
+ age: participant.age ?? '',
40
+ sex: participant.sex ?? '',
41
+ group: participant.group ?? '',
42
+ ses: participant.ses ?? '',
43
+ role: participant.role ?? '',
44
+ education: participant.education ?? '',
45
+ custom: participant.custom ?? '',
46
+ });
13
47
  export default function Participants({ document, participants, onChange, useChat, }) {
14
48
  const { t } = useTranslation('participants');
15
49
  const { dispatchMessage } = useMessageUtils();
16
50
  const [participant, setParticipant] = useState();
17
51
  const [openRemove, setOpenRemove] = useState(false);
18
52
  const [openCreate, setOpenCreate] = useState(false);
19
- const handleSave = (field) => {
20
- if (!field.ref)
53
+ const participantForm = useForm({
54
+ defaultValues: toCoreFormValues({ order: 1, code: '', name: '' }),
55
+ });
56
+ const chatForm = useForm({
57
+ defaultValues: toChatFormValues({ order: 1, code: '', name: '' }),
58
+ });
59
+ const participantFields = useMemo(() => PARTICIPANT_FIELD_DEFS.map((field) => ({
60
+ ...field,
61
+ name: t(`participant.field.${field.attr}`),
62
+ ...(field.tooltip
63
+ ? { tooltipText: t(`participant.field.${field.attr}.tooltip`) }
64
+ : {}),
65
+ })), [t]);
66
+ const chatFields = useMemo(() => CHAT_FIELD_DEFS.map((field) => ({
67
+ ...field,
68
+ name: t(`participant.field.${field.attr}`),
69
+ })), [t]);
70
+ const buildParticipantPayload = () => {
71
+ const coreValues = participantForm.getValues();
72
+ const chatValues = chatForm.getValues();
73
+ const order = typeof coreValues.order === 'string'
74
+ ? Number(coreValues.order) || participant?.order || 1
75
+ : coreValues.order;
76
+ return {
77
+ ...participant,
78
+ ...chatValues,
79
+ ...coreValues,
80
+ order,
81
+ };
82
+ };
83
+ const handleSave = () => {
84
+ if (!participant)
21
85
  return;
22
- ParticipantService.update(field)
86
+ const lookupCode = participant.code;
87
+ const payload = buildParticipantPayload();
88
+ ParticipantService.update(document, lookupCode, payload)
23
89
  .then(() => {
24
90
  dispatchMessage({ key: 'update.success', t });
25
- const { name, value } = field;
26
- const thisParticipants = participants;
27
- const idx = participants.findIndex((p) => p.code === field.ref);
28
- // updates the participant
29
- const thisParticipant = { ...thisParticipants[idx], [name]: value };
30
- thisParticipants[idx] = { ...thisParticipant };
31
- setParticipant(thisParticipant);
91
+ const thisParticipants = [...participants];
92
+ const idx = thisParticipants.findIndex((p) => p.code === lookupCode);
93
+ if (idx >= 0) {
94
+ thisParticipants[idx] = payload;
95
+ }
96
+ setParticipant(payload);
32
97
  onChange(thisParticipants);
33
98
  })
34
99
  .catch((err) => {
@@ -39,9 +104,15 @@ export default function Participants({ document, participants, onChange, useChat
39
104
  if (participants.length > 0)
40
105
  setParticipant(participants[0]);
41
106
  }, []);
107
+ useEffect(() => {
108
+ if (!participant)
109
+ return;
110
+ participantForm.reset(toCoreFormValues(participant));
111
+ chatForm.reset(toChatFormValues(participant));
112
+ }, [participant, participantForm, chatForm]);
42
113
  return (_jsxs("div", { className: "participants-container", children: [_jsxs("div", { className: "header", children: [_jsx("div", { className: "title", children: t('label.title') }), _jsx("div", { className: "actions", children: _jsx(Button, { icon: "add", text: t('button.label.add'), onClick: () => setOpenCreate(true), size: "small", mode: "outlined" }) })] }), _jsx("div", { className: "body", children: participant && (_jsxs(_Fragment, { children: [_jsx(Select, { onChange: (e) => setParticipant(participants[Number(e.target.value)]), value: participant
43
114
  ? participants.findIndex((p) => p.code === participant.code)
44
- : 0, fullWidth: true, className: "select-participant", children: participants.map((el, idx) => (_jsx(MenuItem, { value: idx, children: `${el.code} - ${el.name}` }, idx))) }), _jsx(AppEditable, { translation: "participants", group: "participant", save: handleSave, item: { ...participant, uid: document }, fields: PARTICIPANT_FIELDS, className: "fields", reference: "code" }), useChat && (_jsx(AppEditable, { translation: "participants", group: "participant", save: handleSave, item: { ...participant, uid: document }, fields: CHAT_FIELDS, className: "fields", reference: "code" })), _jsx("div", { className: "footer", children: _jsx(Button, { icon: "delete", text: t('common:button.remove'), size: "small", className: "danger", onClick: () => {
115
+ : 0, fullWidth: true, className: "select-participant", children: participants.map((el, idx) => (_jsx(MenuItem, { value: idx, children: `${el.code} - ${el.name}` }, idx))) }), _jsxs("div", { className: "fields", children: [_jsx(AppForm, { fields: participantFields, form: participantForm }), _jsx(Button, { text: t('common:button.confirm'), onClick: handleSave, size: "small", className: "save-button" })] }), useChat && (_jsxs("div", { className: "fields", children: [_jsx(AppForm, { fields: chatFields, form: chatForm }), _jsx(Button, { text: t('common:button.confirm'), onClick: handleSave, size: "small", className: "save-button" })] })), _jsx("div", { className: "footer", children: _jsx(Button, { icon: "delete", text: t('common:button.remove'), size: "small", className: "danger", onClick: () => {
45
116
  setParticipant(participant);
46
117
  setOpenRemove(true);
47
118
  } }) })] })) }), participant && openRemove && (_jsx(ParticipantRemove, { document: document, onChange: (list) => {
@@ -14,10 +14,18 @@
14
14
  .body {
15
15
  height: 90vh;
16
16
  overflow-y: auto;
17
- padding: 16px;
17
+ padding: var(--spacing-200);
18
18
 
19
19
  > .select-participant {
20
- margin-bottom: 16px;
20
+ margin-bottom: var(--spacing-200);
21
+ }
22
+
23
+ .fields {
24
+ margin-bottom: var(--spacing-200);
25
+
26
+ .save-button {
27
+ margin-top: var(--spacing-200);
28
+ }
21
29
  }
22
30
  }
23
31
 
@@ -1,13 +1,13 @@
1
- import { FormField } from '../../../common/AppEditable/FormField';
2
1
  type Participant = {
3
2
  order: number;
4
3
  code: string;
5
4
  name: string;
6
5
  color?: string;
6
+ separator?: string;
7
7
  role?: string;
8
8
  ses?: string;
9
9
  education?: string;
10
- gender?: string;
10
+ sex?: string;
11
11
  custom?: string;
12
12
  corpus?: string;
13
13
  language?: string;
@@ -18,11 +18,14 @@ export type ParticipantCreateRequest = {
18
18
  code: string;
19
19
  name: string;
20
20
  };
21
+ export type ParticipantUpdateRequest = {
22
+ uid: string;
23
+ code: string;
24
+ participant: Participant;
25
+ };
21
26
  export declare const EMPTY_PARTICIPANT: {
22
27
  order: number;
23
28
  code: string;
24
29
  name: string;
25
30
  };
26
- export declare const PARTICIPANT_FIELDS: FormField[];
27
- export declare const CHAT_FIELDS: FormField[];
28
31
  export default Participant;
@@ -3,25 +3,3 @@ export const EMPTY_PARTICIPANT = {
3
3
  code: '',
4
4
  name: '',
5
5
  };
6
- export const PARTICIPANT_FIELDS = [
7
- { name: 'order', type: 'text', required: true },
8
- { name: 'code', type: 'text', required: true },
9
- { name: 'name', type: 'text', required: false },
10
- { name: 'separator', type: 'text', required: false },
11
- {
12
- name: 'color',
13
- type: 'color',
14
- required: false,
15
- tooltip: true,
16
- },
17
- ];
18
- export const CHAT_FIELDS = [
19
- { name: 'language', type: 'text', required: false },
20
- { name: 'age', type: 'text', required: false },
21
- { name: 'sex', type: 'text', required: false },
22
- { name: 'group', type: 'text', required: false },
23
- { name: 'ses', type: 'text', required: false },
24
- { name: 'role', type: 'text', required: false },
25
- { name: 'education', type: 'text', required: false },
26
- { name: 'custom', type: 'text', required: false },
27
- ];
@@ -5,7 +5,7 @@ export { default as CommentService } from './Comments/CommentService';
5
5
  export { default as Parameters } from './Parameters';
6
6
  export { default as Participants } from './Participants';
7
7
  export type { default as Participant, ParticipantCreateRequest, } from './Participants/types/Participant';
8
- export { EMPTY_PARTICIPANT, PARTICIPANT_FIELDS, CHAT_FIELDS, } from './Participants/types/Participant';
8
+ export { EMPTY_PARTICIPANT, } from './Participants/types/Participant';
9
9
  export { default as SentenceView } from './SentenceView';
10
10
  export { default as TreeView } from './TreeView';
11
11
  export { default as CytoscapeTreeConverter } from './TreeView/cytoscape/CytoscapeTreeConverter';
@@ -3,7 +3,7 @@ export { default as HeaderNotifications } from './Comments/HeaderNotifications';
3
3
  export { default as CommentService } from './Comments/CommentService';
4
4
  export { default as Parameters } from './Parameters';
5
5
  export { default as Participants } from './Participants';
6
- export { EMPTY_PARTICIPANT, PARTICIPANT_FIELDS, CHAT_FIELDS, } from './Participants/types/Participant';
6
+ export { EMPTY_PARTICIPANT, } from './Participants/types/Participant';
7
7
  export { default as SentenceView } from './SentenceView';
8
8
  export { default as TreeView } from './TreeView';
9
9
  export { default as CytoscapeTreeConverter } from './TreeView/cytoscape/CytoscapeTreeConverter';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tycho-components",
3
3
  "private": false,
4
- "version": "0.25.2",
4
+ "version": "0.25.5",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "exports": {
@@ -82,7 +82,7 @@
82
82
  "react-i18next": "^13.0.2",
83
83
  "react-router-dom": "^6.14.2",
84
84
  "react-toastify": "^9.1.3",
85
- "tycho-storybook": "0.9.3",
85
+ "tycho-storybook": "0.9.4",
86
86
  "wavesurfer-react": "^2.2.2",
87
87
  "wavesurfer.js": "^6.6.3"
88
88
  },