This commit is contained in:
Marta Kowalska
2025-10-22 08:13:59 +00:00
commit 80ca059fb8
162 changed files with 16816 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
.text.wx-4RuF7aAO {
display: flex;
align-items: center;
cursor: pointer;
}
.text.wx-4RuF7aAO:hover {
text-decoration: underline;
color: var(--wx-color-link);
}
+15
View File
@@ -0,0 +1,15 @@
import { useContext, useMemo } from 'react';
import { context } from '@svar-ui/react-core';
import './AddTaskCell.css';
export default function AddTaskCell() {
const i18n = useContext(context.i18n);
const _ = useMemo(() => i18n.getGroup('gantt'), []);
const text = useMemo(() => _('Add task'), []);
return (
<div className="wx-4RuF7aAO text" data-action="add-task">
{text}
</div>
);
}
+16
View File
@@ -0,0 +1,16 @@
import { useMemo } from 'react';
import UserStub from './UserStub.jsx';
import { users } from '../data';
function AvatarCell(props) {
const { row } = props;
const user = useMemo(
() => users.find((u) => u.id == row.assigned),
[row.assigned, users],
);
return <UserStub user={user} />;
}
export default AvatarCell;
+66
View File
@@ -0,0 +1,66 @@
.backdrop.wx-QkE5vh0y {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 5;
background: var(--wx-modal-backdrop);
}
.modal.wx-QkE5vh0y {
position: relative;
width: 340px;
padding: 20px;
border-radius: 6px;
box-shadow:
0 4px 4px rgba(0, 0, 0, 0.12),
0 0 10px rgba(0, 0, 0, 0.06);
background-color: var(--wx-background);
font-family: var(--wx-font-family);
font-size: var(--wx-font-size);
color: var(--wx-color-font);
}
.title.wx-QkE5vh0y {
margin: 0;
}
.close.wx-QkE5vh0y {
position: absolute;
top: 20px;
right: 20px;
cursor: pointer;
font-weight: 700;
transition: color 0.15s ease-in;
}
.close.wx-QkE5vh0y:hover {
color: rgb(255, 122, 122);
}
.body.wx-QkE5vh0y {
margin: 20px 0 0 0;
}
.button.wx-QkE5vh0y {
padding: 10px;
margin: 1.5em 0 0 0;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 2px;
font-family: var(--wx-font-family);
font-size: var(--wx-font-size);
border-radius: 3px;
cursor: pointer;
}
.button.wx-QkE5vh0y:focus {
outline: none;
opacity: 0.7;
}
.danger.wx-QkE5vh0y {
color: var(--wx-color-danger-font);
background-color: var(--wx-color-danger);
}
+141
View File
@@ -0,0 +1,141 @@
import { useEffect, useRef, useState } from 'react';
import {
Field,
Text,
TextArea,
Select,
Slider,
DatePicker,
} from '@svar-ui/react-core';
import { useWritableProp } from '@svar-ui/lib-react';
import './Form.css';
export default function Form(props) {
const [task, setTask] = useWritableProp(props.task);
const { taskTypes, onAction } = props;
const nodeRef = useRef(null);
const [left, setLeft] = useState();
const [top, setTop] = useState();
useEffect(() => {
if (nodeRef.current) {
setLeft((window.innerWidth - nodeRef.current.offsetWidth) / 2);
setTop((window.innerHeight - nodeRef.current.offsetHeight) / 2);
}
}, []);
function deleteTask() {
onAction && onAction({ action: 'delete-task', data: { id: task.id } });
onAction && onAction({ action: 'close-form' });
}
function onClose() {
onAction && onAction({ action: 'close-form' });
}
function handleChange({ value }, key) {
let t = task;
if (key === 'type' && value === 'milestone') {
delete t.end;
t.duration = 0;
} else if (t.start > t.end) {
t.start = t.end;
t.duration = 1;
t.end = 0;
}
t = {
...t,
[key]: value,
};
setTask(t);
onAction &&
onAction({
action: 'update-task',
data: { id: t.id, task: t },
});
}
return (
<div className="wx-QkE5vh0y backdrop">
<div className="wx-QkE5vh0y modal" style={{ left, top }} ref={nodeRef}>
<div className="wx-QkE5vh0y header">
<h3 className="wx-QkE5vh0y title">Edit task</h3>
<i className="wx-QkE5vh0y close wxi-close" onClick={onClose}></i>
</div>
<div className="wx-QkE5vh0y body">
<Field label="Name">
{({ id }) => (
<Text
id={id}
focus={true}
value={task.text}
onChange={(ev) => handleChange(ev, 'text')}
/>
)}
</Field>
<Field label="Description">
{({ id }) => (
<TextArea
id={id}
value={task.details}
onChange={(ev) => handleChange(ev, 'details')}
/>
)}
</Field>
{taskTypes.length > 1 ? (
<Field label="Type">
{({ id }) => (
<Select
id={id}
value={task.type}
options={taskTypes}
onChange={(ev) => handleChange(ev, 'type')}
/>
)}
</Field>
) : null}
<Field label="Start date">
{({ id }) => (
<DatePicker
id={id}
value={task.start}
onChange={(ev) => handleChange(ev, 'start')}
/>
)}
</Field>
{task.type !== 'milestone' ? (
<>
<Field label="End date">
{({ id }) => (
<DatePicker
id={id}
value={task.end}
onChange={(ev) => handleChange(ev, 'end')}
/>
)}
</Field>
<Field label={`Progress: ${task.progress}%`}>
{({ id }) => (
<Slider
id={id}
value={task.progress}
onChange={(ev) => handleChange(ev, 'progress')}
/>
)}
</Field>
</>
) : null}
<button className="wx-QkE5vh0y button danger" onClick={deleteTask}>
Delete
</button>
</div>
</div>
</div>
);
}
+23
View File
@@ -0,0 +1,23 @@
button.wx-BzRGIq8x {
font-size: 10px;
position: relative;
z-index: 2;
font: var(--wx-gantt-bar-font);
}
.text-right.wx-BzRGIq8x {
left: 100%;
top: -2px;
}
.text-left.wx-BzRGIq8x {
right: 100%;
top: -2px;
}
.text-right.wx-BzRGIq8x,
.text-left.wx-BzRGIq8x,
button.wx-BzRGIq8x {
padding: 0 2px;
font-size: 12px;
}
+35
View File
@@ -0,0 +1,35 @@
import './MyTaskContent.css';
function MyTaskContent({ data, onAction }) {
function doClick(ev) {
ev.stopPropagation();
onAction({
action: 'custom-click',
data: {
clicked: !data.clicked,
id: data.id,
},
});
}
return (
<>
{data.type !== 'milestone' ? (
<>
<div className="wx-BzRGIq8x wx-text-out text-right">
{data.text || ''}
</div>
<button className="wx-BzRGIq8x" onClick={doClick}>
{data.clicked ? 'Was clicked' : 'Click Me'}
</button>
</>
) : (
<div className="wx-BzRGIq8x wx-text-out text-left">
{data.text || ''}
</div>
)}
</>
);
}
export default MyTaskContent;
+21
View File
@@ -0,0 +1,21 @@
.data.wx-SfydHtKO {
white-space: nowrap;
background-color: var(--wx-tooltip-background);
padding: 3px 8px;
}
.text.wx-SfydHtKO {
font-family: var(--wx-font-family);
color: var(--wx-color-primary-font);
font-size: 13px;
text-transform: capitalize;
margin-bottom: 5px;
}
.text:last-child.wx-SfydHtKO {
margin-bottom: 0;
}
.caption.wx-SfydHtKO {
font-weight: 700;
}
+32
View File
@@ -0,0 +1,32 @@
import { format } from 'date-fns';
import './MyTooltipContent.css';
function MyTooltipContent(props) {
const { data } = props;
const mask = 'yyyy.MM.dd';
return (
<>
{data ? (
<div className="wx-SfydHtKO data">
<div className="wx-SfydHtKO text">
<span className="wx-SfydHtKO caption">{data.type}: </span>
{data.text}
</div>
<div className="wx-SfydHtKO text">
<span className="wx-SfydHtKO caption">start: </span>
{format(data.start, mask)}
</div>
{data.end ? (
<div className="wx-SfydHtKO text">
<span className="wx-SfydHtKO caption">end: </span>
{format(data.end, mask)}
</div>
) : null}
</div>
) : null}
</>
);
}
export default MyTooltipContent;
+10
View File
@@ -0,0 +1,10 @@
.text.wx-4qJ64Gcp,
.date.wx-4qJ64Gcp {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.date.wx-4qJ64Gcp {
color: #5f5f5f;
font-size: 12px;
}
+13
View File
@@ -0,0 +1,13 @@
import { useMemo } from 'react';
import './NameAndDateCell.css';
export default function NameAndDateCell({ row }) {
const date = useMemo(() => row.start.toLocaleDateString(), [row.start]);
return (
<>
<div className="wx-4qJ64Gcp text">{row.text}</div>
<div className="wx-4qJ64Gcp date">{date}</div>
</>
);
}
+25
View File
@@ -0,0 +1,25 @@
.container.wx-Md6hA5Em {
display: flex;
align-items: center;
padding: 0 4px;
}
.avatar.wx-Md6hA5Em {
width: 40px;
}
.user-avatar.wx-Md6hA5Em {
width: 30px;
height: 30px;
border-radius: 50%;
background-color: #dfe2e6;
text-align: center;
}
.user-photo.wx-Md6hA5Em {
display: block;
width: 100%;
height: 100%;
border-radius: 50%;
background-color: #dfe2e6;
}
+27
View File
@@ -0,0 +1,27 @@
import { useMemo } from 'react';
import './UserStub.css';
function UserStub(props) {
const { user } = props;
const url = 'https://svar.dev/demos/grid/assets/avatars/';
const avatar = useMemo(
() => (user ? `${url}${user.label.replace(' ', '_')}.png` : ''),
[user],
);
return (
<div className="wx-Md6hA5Em container">
<div className="wx-Md6hA5Em avatar">
{avatar ? (
<div className="wx-Md6hA5Em user-avatar">
<img className="wx-Md6hA5Em user-photo" alt="" src={avatar} />
</div>
) : null}
</div>
<div>{user?.label ?? ''}</div>
</div>
);
}
export default UserStub;
+20
View File
@@ -0,0 +1,20 @@
import { Combo } from '@svar-ui/react-core';
import UserStub from './UserStub.jsx';
function UsersCustomCombo(props) {
const { value, options, onChange } = props;
return (
<Combo
options={options}
value={value}
onChange={onChange}
clear
placeholder="Assign to the person"
>
{({ option }) => <UserStub user={option} />}
</Combo>
);
}
export default UsersCustomCombo;