Files
CT/src/Views/Objects/objectPane.tsx
T

231 lines
6.5 KiB
TypeScript
Raw Normal View History

2024-07-10 19:07:58 +02:00
import * as React from "bloatless-react";
import { Chat, MessageObject } from "../../Model/chatModel";
2024-07-11 00:56:06 +02:00
2024-07-11 18:54:56 +02:00
import { AllObjectsView } from "./allObjectsView";
2024-07-12 00:43:58 +02:00
import { KanbanView } from "./kanbanView";
2024-07-11 18:54:56 +02:00
import { NoteObjectsView } from "./noteObjectsView";
2024-07-11 13:05:02 +02:00
import { ObjectDetailModal } from "./objectDetailModal";
import { ObjectGridView } from "./objectGridView";
2024-07-12 14:30:30 +02:00
import { StatusView } from "./statusView";
2024-07-11 22:27:15 +02:00
import { icons } from "../../icons";
2024-07-11 01:24:06 +02:00
import { translation } from "../../translations";
2024-07-10 19:07:58 +02:00
2024-07-11 18:54:56 +02:00
export const viewTypes = {
all: [translation.viewAll, "grid_view"],
2024-07-11 22:27:15 +02:00
notes: [translation.viewNotes, icons.noteContent],
2024-07-12 00:43:58 +02:00
kanban: [translation.viewKanban, "view_kanban"],
2024-07-12 14:30:30 +02:00
status: [translation.viewStatus, icons.status],
2024-07-11 18:54:56 +02:00
};
2024-07-12 00:43:58 +02:00
export function ObjectPane(chat: Chat) {
2024-07-11 18:54:56 +02:00
// selection and modal
const isShowingObjectModal = new React.State(false);
2024-07-13 17:13:59 +02:00
const isShowingFilterModel = new React.State(false);
const selectedObject = new React.State<MessageObject | undefined>(undefined);
2024-07-13 17:13:59 +02:00
// methods
function createObject() {
const newObject = chat.createObjectFromTitle("");
chat.addObjectAndSend(newObject);
selectedObject.value = newObject;
isShowingObjectModal.value = true;
}
function showFilters() {
isShowingFilterModel.value = true;
}
function closeFilters() {
isShowingFilterModel.value = false;
}
// filter
const filterInput = new React.State("");
const appliedFilter = new React.State("");
const resultCount = new React.State(0);
const resultText = React.createProxyState([appliedFilter, resultCount], () =>
translation.searchTitleText(appliedFilter.value, resultCount.value)
);
2024-07-15 21:58:32 +02:00
const showingMessageObjects = new React.MapState<MessageObject>();
2024-07-13 17:13:59 +02:00
const cannotReset = React.createProxyState(
[appliedFilter],
() => appliedFilter.value == ""
);
function resetFilter() {
filterInput.value = "";
applyFilter();
}
function applyFilter() {
appliedFilter.value = filterInput.value;
const allObjects = [...chat.objects.value.entries()];
const matchingObjects = allObjects.filter((entry) => {
2024-07-15 21:58:32 +02:00
return checkMatchesFilter(entry[1]);
});
2024-07-13 17:13:59 +02:00
resultCount.value = matchingObjects.length;
2024-07-15 21:58:32 +02:00
showingMessageObjects.clear();
matchingObjects.forEach((entry) => showingMessageObjects.set(...entry));
}
chat.objects.handleAddition(messageObject => {
if (checkMatchesFilter(messageObject) == false) return;
showingMessageObjects.set(messageObject.id, messageObject);
chat.objects.handleRemoval(messageObject, () => {
showingMessageObjects.remove(messageObject.id);
})
})
function checkMatchesFilter(messageObject: MessageObject) {
let objectTitle = messageObject.title || translation.untitledObject;
const regex = new RegExp(appliedFilter.value.toLowerCase());
return regex.test(objectTitle.toLowerCase());
2024-07-14 20:08:01 +02:00
}
2024-07-15 21:58:32 +02:00
applyFilter();
2024-07-13 17:13:59 +02:00
// view
2024-07-11 16:45:45 +02:00
const objectModal = React.createProxyState(
[chat.objects, selectedObject],
() => {
if (selectedObject.value == undefined) return <div></div>;
return ObjectDetailModal(
chat,
selectedObject.value,
isShowingObjectModal
);
}
);
const mainView = React.createProxyState([chat.viewType], () => {
2024-07-11 18:54:56 +02:00
function getViewFunction() {
switch (chat.viewType.value) {
2024-07-11 18:54:56 +02:00
case "notes":
return NoteObjectsView;
2024-07-12 00:43:58 +02:00
case "kanban":
return KanbanView;
2024-07-12 14:30:30 +02:00
case "status":
return StatusView;
2024-07-11 18:54:56 +02:00
default:
return AllObjectsView;
}
}
2024-07-13 17:13:59 +02:00
return getViewFunction()(
chat,
2024-07-15 21:58:32 +02:00
showingMessageObjects,
2024-07-13 17:13:59 +02:00
selectedObject,
isShowingObjectModal
);
2024-07-11 18:54:56 +02:00
});
2024-07-10 19:07:58 +02:00
return (
2024-07-13 15:10:54 +02:00
<div class="chat-object-view flex-column scroll-no padding-0">
2024-07-11 18:54:56 +02:00
<div class="flex-row align-center border-bottom">
2024-07-11 17:07:06 +02:00
<button
2024-07-11 18:54:56 +02:00
class="primary height-100"
2024-07-11 17:07:06 +02:00
on:click={createObject}
aria-label={translation.createObject}
>
<span class="icon">add</span>
</button>
2024-07-11 18:54:56 +02:00
<div class="padding-sm flex flex-row gap justify-center scroll-h width-100">
{...Object.keys(viewTypes).map((key: any) =>
ViewTypeToggle(key, chat.viewType)
2024-07-11 18:54:56 +02:00
)}
</div>
2024-07-13 17:13:59 +02:00
<button
class="height-100"
aria-label={translation.filterObjects}
on:click={showFilters}
>
<span class="icon">filter_list</span>
2024-07-11 18:54:56 +02:00
</button>
2024-07-11 17:07:06 +02:00
</div>
2024-07-12 14:30:30 +02:00
<div
2024-07-13 15:10:54 +02:00
class="object-content width-100 height-100 flex scroll-no"
2024-07-12 14:30:30 +02:00
children:set={mainView}
></div>
2024-07-13 17:13:59 +02:00
<div class="modal" toggle:open={isShowingFilterModel}>
2024-07-15 21:58:32 +02:00
<div style="max-width: 2084px">
2024-07-15 22:04:29 +02:00
<main class="gap">
2024-07-13 17:13:59 +02:00
<h2>{translation.filterObjects}</h2>
2024-07-13 17:18:12 +02:00
<div class="flex-column">
<label class="tile">
<span class="icon">search</span>
<div>
<span>{translation.searchByTitle}</span>
<input
bind:value={filterInput}
on:enter={applyFilter}
placeholder={translation.searchByTitlePlaceholder}
></input>
</div>
</label>
</div>
2024-07-13 17:13:59 +02:00
<div class="flex-row width-input">
<button
class="width-50 flex"
on:click={resetFilter}
toggle:disabled={cannotReset}
>
{translation.reset}
</button>
<button class="width-50 flex primary" on:click={applyFilter}>
{translation.search}
<span class="icon">arrow_forward</span>
</button>
</div>
<hr></hr>
2024-07-15 22:04:29 +02:00
<b class="secondary" subscribe:innerText={resultText}></b>
{ObjectGridView(
chat,
2024-07-15 21:58:32 +02:00
showingMessageObjects,
selectedObject,
isShowingObjectModal
)}
2024-07-13 17:13:59 +02:00
</main>
<button on:click={closeFilters}>
{translation.close}
<span class="icon">close</span>
</button>
</div>
</div>
<div children:set={objectModal}></div>
2024-07-10 19:07:58 +02:00
</div>
);
}
2024-07-11 18:54:56 +02:00
function ViewTypeToggle(
key: keyof typeof viewTypes,
selection: React.State<keyof typeof viewTypes>
) {
const [label, icon] = viewTypes[key];
function select() {
selection.value = key;
}
const isSelected = React.createProxyState(
[selection],
() => selection.value == key
);
return (
<button aria-label={label} on:click={select} toggle:selected={isSelected}>
<span class="icon">{icon}</span>
</button>
);
}