[mod] zahlreiche Anpassungen und Verbesserungen

This commit is contained in:
fenris 2025-10-20 13:16:00 +02:00
parent d8d15b03ac
commit 25291b373a
21 changed files with 1338 additions and 770 deletions

View file

@ -21,6 +21,7 @@
"common.cancel": "abbrechen", "common.cancel": "abbrechen",
"common.login": "anmelden", "common.login": "anmelden",
"common.logout": "abmelden", "common.logout": "abmelden",
"common.confirm_deletion": "sicher?",
"access_level.none": "nichts", "access_level.none": "nichts",
"access_level.view": "nur lesen", "access_level.view": "nur lesen",
"access_level.edit": "lesen und bearbeiten", "access_level.edit": "lesen und bearbeiten",
@ -51,6 +52,7 @@
"widget.weekview.controls.year": "Jahr", "widget.weekview.controls.year": "Jahr",
"widget.weekview.controls.week": "Woche", "widget.weekview.controls.week": "Woche",
"widget.weekview.controls.count": "Anzahl", "widget.weekview.controls.count": "Anzahl",
"widget.weekview.controls.vertical": "senkrecht",
"widget.weekview.controls.apply": "Laden", "widget.weekview.controls.apply": "Laden",
"widget.calendar_edit.actions.add": "Kalender anlegen", "widget.calendar_edit.actions.add": "Kalender anlegen",
"widget.calendar_edit.actions.change": "ändern", "widget.calendar_edit.actions.change": "ändern",

View file

@ -21,6 +21,7 @@
"common.cancel": "cancel", "common.cancel": "cancel",
"common.login": "login", "common.login": "login",
"common.logout": "logout", "common.logout": "logout",
"common.confirm_deletion": "sure?",
"access_level.none": "none", "access_level.none": "none",
"access_level.view": "read only", "access_level.view": "read only",
"access_level.edit": "read and write", "access_level.edit": "read and write",
@ -48,9 +49,10 @@
"calendar.access.default_level": "default", "calendar.access.default_level": "default",
"calendar.access.attributed": "attributed", "calendar.access.attributed": "attributed",
"widget.listview.add": "add event", "widget.listview.add": "add event",
"widget.weekview.controls.year": "Year", "widget.weekview.controls.year": "year",
"widget.weekview.controls.week": "Week", "widget.weekview.controls.week": "week",
"widget.weekview.controls.count": "Count", "widget.weekview.controls.count": "count",
"widget.weekview.controls.vertical": "vertical",
"widget.weekview.controls.apply": "Load", "widget.weekview.controls.apply": "Load",
"widget.calendar_edit.actions.add": "add calendar", "widget.calendar_edit.actions.add": "add calendar",
"widget.calendar_edit.actions.change": "change", "widget.calendar_edit.actions.change": "change",

View file

@ -209,4 +209,24 @@ namespace _dali.helpers
); );
} }
/**
*/
export function event_color(
hue : float
)
:
string
{
return lib_plankton.color.output_hex(
lib_plankton.color.make_hsv(
{
"hue": hue,
"saturation": 0.375,
"value": 0.375,
}
),
);
}
} }

View file

@ -20,15 +20,13 @@ document.addEventListener(
) )
} }
); );
</script> </script>
{{templates}}
</head> </head>
<div id="overlay">
<div id="overlay_content">
</div>
</div>
<body> <body>
<div id="overlay">
<div id="overlay_content">
</div>
</div>
<header> <header>
</header> </header>
<main> <main>

View file

@ -0,0 +1,22 @@
.caldav-conf-section
{
margin-bottom: 16px;
}
.caldav-conf-section-label
{
margin-left: 16px;
display: block;
font-weight: bold;
text-transform: capitalize;
}
.caldav-conf-section-value
{
margin-left: 32px;
}
.caldav-conf-section-value-regular
{
font-family: monospace;
}

View file

@ -9,14 +9,6 @@ namespace _dali.pages.overview
// params // params
const view_mode : _dali.enum_view_mode = _dali.helpers.view_mode_determine(parameters["mode"] ?? "auto"); const view_mode : _dali.enum_view_mode = _dali.helpers.view_mode_determine(parameters["mode"] ?? "auto");
/**
* @todo ordentlich machen (nicht nur week und list)
*/
const set_view_mode = (view_mode) => {
const compact : boolean = (view_mode !== _dali.enum_view_mode.week);
target_element.querySelector("#overview").classList.toggle("overview-compact", compact);
};
// exec // exec
target_element.innerHTML = await _dali.helpers.template_coin( target_element.innerHTML = await _dali.helpers.template_coin(
"overview", "overview",
@ -25,9 +17,366 @@ namespace _dali.pages.overview
} }
); );
let widget_mode_switcher : lib_plankton.zoo_widget.interface_widget;
let widget_sources : _dali.widgets.sources.class_widget_sources;
let widget_weekview : _dali.widgets.weekview.class_widget_weekview;
let widget_listview : _dali.widgets.listview.class_widget_listview;
/**
* @todo ordentlich machen (nicht nur week und list)
*/
function set_view_mode
(
view_mode : _dali.enum_view_mode
)
: void
{
const compact : boolean = (view_mode !== _dali.enum_view_mode.week);
target_element.querySelector("#overview").classList.toggle("overview-compact", compact);
}
/**
* @todo add return type
*/
async function get_available_calendars(
)
{
return (
(await _dali.model.calendar_list())
.filter(
(entry) => (
(entry.access_level === _dali.enum_access_level.edit)
||
(entry.access_level === _dali.enum_access_level.admin)
)
)
);
}
/**
* @todo add return type
*/
async function get_entries(
from_pit : lib_plankton.pit.type_pit,
to_pit : lib_plankton.pit.type_pit,
calendar_ids : Array<_dali.type_calendar_id>
)
: Promise<Array<_dali.type_event_object_extended>>
{
/**
* @todo do NOT wait?
*/
await _dali.model.sync_events(
{
"from": from_pit,
"to": to_pit,
},
{
"calendar_ids": calendar_ids,
}
);
/**
* @todo filter
*/
return _dali.model.event_list();
}
/**
* @todo update listview
*/
async function update_sources_and_entries(
{
"priviliged": priviliged = null,
}
:
{
priviliged ?: (null | boolean);
}
=
{
}
)
: Promise<void>
{
await widget_sources.update({"priviliged": priviliged});
await widget_weekview.update_entries();
}
/**
* @todo use priviliged?
* @todo update listview
*/
async function update_entries(
{
"priviliged": priviliged = null,
}
:
{
priviliged ?: (null | boolean);
}
=
{
}
)
: Promise<void>
{
await widget_weekview.update_entries();
}
/**
*/
async function action_create_calendar(
)
: Promise<void>
{
const widget = new _dali.widgets.calendar_edit.class_widget_calendar_edit(
await _dali.model.user_list(),
{
"read_only": false,
"action_cancel": () => {
_dali.overlay.toggle({"mode": false});
},
"action_add": (calendar_object) => {
_dali.model.calendar_add(
calendar_object
)
.then(
() => {
update_sources_and_entries();
_dali.overlay.toggle({"mode": false});
}
)
.catch(
(reason) => {
lib_plankton.log.warning(
"dali.overview.calendar_add_error",
{"reason": String(reason)}
);
}
);
},
"initial_value": null,
}
);
_dali.overlay.clear();
_dali.overlay.toggle({"mode": true});
await widget.load(_dali.overlay.get_content_element());
}
/**
* @todo unterschiedliches Verhalten bei Anmeldung?
*/
async function action_create_event(
{
"date": date = lib_plankton.pit.to_datetime(lib_plankton.pit.now()).date,
}
:
{
date ?: lib_plankton.pit.type_date;
}
=
{
}
)
: Promise<void>
{
const widget = new _dali.widgets.event_edit.class_widget_event_edit(
(await get_available_calendars()),
{
"calendar_id": null,
"event_name": "",
"event_begin": lib_plankton.call.convey(
date,
[
x => ({
"timezone_shift": 0,
"date": date,
"time": {"hour": 12, "minute": 0, "second": 0}
}),
lib_plankton.pit.from_datetime,
x => lib_plankton.pit.shift_hour(x, 0),
lib_plankton.pit.to_datetime,
]
),
"event_end": lib_plankton.call.convey(
date,
[
x => ({
"timezone_shift": 0,
"date": date,
"time": {"hour": 12, "minute": 0, "second": 0}
}),
lib_plankton.pit.from_datetime,
x => lib_plankton.pit.shift_hour(x, +1),
lib_plankton.pit.to_datetime,
]
),
"event_location": null,
"event_link": null,
"event_description": null,
},
{
"read_only": false,
"action_cancel": () => {
_dali.overlay.toggle({"mode": false});
},
"action_add": (data) => {
_dali.model.event_add(
data.calendar_id,
{
"name": data.event_name,
"begin": data.event_begin,
"end": data.event_end,
"location": data.event_location,
"link": data.event_link,
"description": data.event_description,
}
)
.then(
() => {
update_entries();
_dali.overlay.toggle({"mode": false});
}
)
.catch(
(reason) => {
// todo
}
);
},
}
);
_dali.overlay.clear();
_dali.overlay.toggle({"mode": true});
await widget.load(_dali.overlay.get_content_element());
}
/**
*/
async function action_select_event
(
event_key : _dali.type_event_key
)
: Promise<void>
{
const event_object_extended : _dali.type_event_object_extended = await _dali.model.event_get(event_key);
const calendar_id = event_object_extended.calendar_id;
const access_level = event_object_extended.access_level;
const event_id = event_object_extended.event_id;
/*
if (! _dali.is_logged_in())
{
// do nothing
}
else
{
}
*/
let read_only : boolean;
switch (access_level)
{
case _dali.enum_access_level.none:
{
throw (new Error("this event should not be visible"));
break;
}
case _dali.enum_access_level.view:
{
read_only = true;
break;
}
case _dali.enum_access_level.edit:
case _dali.enum_access_level.admin:
{
read_only = false;
break;
}
}
(async () => {
const event_object : _dali.type_event_object = await _dali.backend.calendar_event_get(
calendar_id,
event_id
);
const widget = new _dali.widgets.event_edit.class_widget_event_edit(
(await get_available_calendars()),
{
"calendar_id": calendar_id,
"event_name": event_object.name,
"event_begin": event_object.begin,
"event_end": event_object.end,
"event_location": event_object.location,
"event_link": event_object.link,
"event_description": event_object.description,
},
{
"read_only": read_only,
"action_cancel": () => {
_dali.overlay.toggle({"mode": false});
},
"action_change": (data) => {
_dali.model.event_change(
event_key,
{
"name": data.event_name,
"begin": data.event_begin,
"end": data.event_end,
"location": data.event_location,
"link": data.event_link,
"description": data.event_description,
}
)
.then(
() => {
update_entries();
_dali.overlay.toggle({"mode": false});
}
)
.catch(
(reason) => {
lib_plankton.log.warning(
"dali.overview.event_change.error",
{"reason": String(reason)}
);
}
);
},
"action_remove": () => {
_dali.model.event_remove(
event_key
)
.then(
() => {
update_entries();
_dali.overlay.toggle({"mode": false});
}
)
.catch(
(reason) => {
lib_plankton.log.warning(
"dali.overview.event_remove_error",
{"reason": String(reason)}
);
}
);
},
}
);
_dali.overlay.clear();
_dali.overlay.toggle({"mode": true});
await widget.load(_dali.overlay.get_content_element());
}) ();
}
// hint
{
const dom_hint = target_element.querySelector("#overview-hint");
dom_hint.textContent = lib_plankton.translate.get("page.overview.login_hint");
dom_hint.classList.toggle("overview-hint-hidden", _dali.is_logged_in());
}
// mode switcher // mode switcher
{ {
const widget_mode_switcher : lib_plankton.zoo_widget.interface_widget = new _dali.widgets.mode_switcher.class_widget_mode_switcher( widget_mode_switcher = new _dali.widgets.mode_switcher.class_widget_mode_switcher(
[ [
{ {
"mode": _dali.enum_view_mode.week, "mode": _dali.enum_view_mode.week,
@ -57,83 +406,13 @@ namespace _dali.pages.overview
await widget_mode_switcher.load(target_element.querySelector("#overview-mode")); await widget_mode_switcher.load(target_element.querySelector("#overview-mode"));
} }
let widget_sources : _dali.widgets.sources.class_widget_sources;
let widget_weekview : _dali.widgets.weekview.class_widget_weekview;
let widget_listview : _dali.widgets.listview.class_widget_listview;
const get_available_calendars = async () => {
return (
(await _dali.model.calendar_list())
.filter(
(entry) => (
(entry.access_level === _dali.enum_access_level.edit)
||
(entry.access_level === _dali.enum_access_level.admin)
)
)
);
}
/**
* @todo update listview
*/
const update_sources_and_entries = async (priviliged = null) => {
await widget_sources.update({"priviliged": priviliged});
await widget_weekview.update_entries();
};
/**
* @todo update listview
*/
const update_entries = async (priviliged = null) => {
await widget_weekview.update_entries();
};
// hint
{
const dom_hint = target_element.querySelector("#overview-hint");
dom_hint.textContent = lib_plankton.translate.get("page.overview.login_hint");
dom_hint.classList.toggle("overview-hint-hidden", _dali.is_logged_in());
}
// sources // sources
{ {
widget_sources = new _dali.widgets.sources.class_widget_sources( widget_sources = new _dali.widgets.sources.class_widget_sources(
_dali.model.calendar_list, _dali.model.calendar_list,
{ {
"initial_priviliged": _dali.is_logged_in(), "initial_priviliged": _dali.is_logged_in(),
"action_create": () => { "action_create": action_create_calendar,
(async () => {
const widget = new _dali.widgets.calendar_edit.class_widget_calendar_edit(
await _dali.model.user_list(),
{
"read_only": false,
"action_cancel": () => {
_dali.overlay.toggle({"mode": false});
},
"action_add": (calendar_object) => {
_dali.model.calendar_add(
calendar_object
)
.then(
() => {
update_sources_and_entries();
_dali.overlay.toggle({"mode": false});
}
)
.catch(
(reason) => {
lib_plankton.log.warning(
"dali.overview.calendar_add_error",
{"reason": String(reason)}
);
}
);
},
"initial_value": null,
}
);
_dali.overlay.clear();
_dali.overlay.toggle({"mode": true});
await widget.load(_dali.overlay.get_content_element());
}) ();
},
"action_open": (entry) => { "action_open": (entry) => {
let read_only : boolean; let read_only : boolean;
switch (entry.access_level) switch (entry.access_level)
@ -198,268 +477,50 @@ namespace _dali.pages.overview
await widget.load(_dali.overlay.get_content_element()); await widget.load(_dali.overlay.get_content_element());
}) (); }) ();
}, },
"action_toggle_visibility": (entry) => { "action_toggle_visibility": (entry, mode) => {
widget_weekview.toggle_visibility(entry.id); widget_weekview.toggle_visibility(entry.id, {"mode": mode});
widget_listview.toggle_visibility(entry.id); widget_listview.toggle_visibility(entry.id, {"mode": mode});
}, },
} }
); );
await widget_sources.load(target_element.querySelector("#overview-pane-left")); await widget_sources.load(target_element.querySelector("#overview-pane-left"));
} }
// events
// weekview
{ {
const get_entries = async (from_pit, to_pit, calendar_ids) => { widget_weekview = (
/** new _dali.widgets.weekview.class_widget_weekview(
* @todo do NOT wait? get_entries,
*/
await _dali.model.sync_events(
{ {
"from": from_pit, "action_select_event": (event_key) => action_select_event(event_key),
"to": to_pit, "action_select_day": (date) => action_create_event({"date": date}),
},
{
"calendar_ids": calendar_ids,
} }
); )
/**
* @todo filter
*/
return _dali.model.event_list();
};
const action_select_event = async (event_key) => {
const event_object_extended : _dali.type_event_object_extended = await _dali.model.event_get(event_key);
const calendar_id = event_object_extended.calendar_id;
const access_level = event_object_extended.access_level;
const event_id = event_object_extended.event_id;
/*
if (! _dali.is_logged_in())
{
// do nothing
}
else
{
}
*/
let read_only : boolean;
switch (access_level)
{
case _dali.enum_access_level.none:
{
throw (new Error("this event should not be visible"));
break;
}
case _dali.enum_access_level.view:
{
read_only = true;
break;
}
case _dali.enum_access_level.edit:
case _dali.enum_access_level.admin:
{
read_only = false;
break;
}
}
(async () => {
const event_object : _dali.type_event_object = await _dali.backend.calendar_event_get(
calendar_id,
event_id
);
const widget = new _dali.widgets.event_edit.class_widget_event_edit(
(await get_available_calendars()),
{
"calendar_id": calendar_id,
"event_name": event_object.name,
"event_begin": event_object.begin,
"event_end": event_object.end,
"event_location": event_object.location,
"event_link": event_object.link,
"event_description": event_object.description,
},
{
"read_only": read_only,
"action_cancel": () => {
_dali.overlay.toggle({"mode": false});
},
"action_change": (data) => {
_dali.model.event_change(
event_key,
{
"name": data.event_name,
"begin": data.event_begin,
"end": data.event_end,
"location": data.event_location,
"link": data.event_link,
"description": data.event_description,
}
)
.then(
() => {
update_entries();
_dali.overlay.toggle({"mode": false});
}
)
.catch(
(reason) => {
lib_plankton.log.warning(
"dali.overview.event_change.error",
{"reason": String(reason)}
);
}
);
},
"action_remove": () => {
_dali.model.event_remove(
event_key
)
.then(
() => {
update_entries();
_dali.overlay.toggle({"mode": false});
}
)
.catch(
(reason) => {
lib_plankton.log.warning(
"dali.overview.event_remove_error",
{"reason": String(reason)}
);
}
);
},
}
);
_dali.overlay.clear();
_dali.overlay.toggle({"mode": true});
await widget.load(_dali.overlay.get_content_element());
}) ();
};
// listview
{
widget_listview = (
new _dali.widgets.listview.class_widget_listview(
get_entries,
{
"action_select_event": action_select_event,
"action_add": () => {
lib_plankton.zoo_page.set(
{
"name": "event_add",
"parameters": {
"calendar_id": null,
"year": null,
"month": null,
"day": null,
}
}
);
},
}
)
);
await widget_listview.load(target_element.querySelector("#overview-pane-right-listview"));
}
// weekview
{
widget_weekview = (
new _dali.widgets.weekview.class_widget_weekview(
get_entries,
{
"action_select_event": action_select_event,
"action_select_day": (date) => {
/*
if (! _dali.is_logged_in())
{
// do nothing
}
else
{
}
*/
(async () => {
const widget = new _dali.widgets.event_edit.class_widget_event_edit(
(await get_available_calendars()),
{
"calendar_id": null,
"event_name": "",
"event_begin": lib_plankton.call.convey(
date,
[
x => ({
"timezone_shift": 0,
"date": date,
"time": {"hour": 12, "minute": 0, "second": 0}
}),
lib_plankton.pit.from_datetime,
x => lib_plankton.pit.shift_hour(x, 0),
lib_plankton.pit.to_datetime,
]
),
"event_end": lib_plankton.call.convey(
date,
[
x => ({
"timezone_shift": 0,
"date": date,
"time": {"hour": 12, "minute": 0, "second": 0}
}),
lib_plankton.pit.from_datetime,
x => lib_plankton.pit.shift_hour(x, +1),
lib_plankton.pit.to_datetime,
]
),
"event_location": null,
"event_link": null,
"event_description": null,
},
{
"read_only": false,
"action_cancel": () => {
_dali.overlay.toggle({"mode": false});
},
"action_add": (data) => {
_dali.model.event_add(
data.calendar_id,
{
"name": data.event_name,
"begin": data.event_begin,
"end": data.event_end,
"location": data.event_location,
"link": data.event_link,
"description": data.event_description,
}
)
.then(
() => {
update_entries();
_dali.overlay.toggle({"mode": false});
}
)
.catch(
(reason) => {
// todo
}
);
},
}
);
_dali.overlay.clear();
_dali.overlay.toggle({"mode": true});
await widget.load(_dali.overlay.get_content_element());
}) ();
},
}
)
);
await widget_weekview.load(target_element.querySelector("#overview-pane-right-weekview"));
}
_dali.model.listen_reset(
async (priviliged) => {
update_sources_and_entries(priviliged);
target_element.querySelector("#overview-hint").classList.toggle("overview-hint-hidden", priviliged);
}
); );
await widget_weekview.load(target_element.querySelector("#overview-pane-right-weekview"));
} }
// listview
{
widget_listview = (
new _dali.widgets.listview.class_widget_listview(
get_entries,
{
"action_select": (event_key) => action_select_event(event_key),
"action_add": () => action_create_event(),
}
)
);
await widget_listview.load(target_element.querySelector("#overview-pane-right-listview"));
}
_dali.model.listen_reset(
async (priviliged) => {
update_sources_and_entries({"priviliged": priviliged});
target_element.querySelector("#overview-hint").classList.toggle("overview-hint-hidden", priviliged);
}
);
return Promise.resolve<void>(undefined); return Promise.resolve<void>(undefined);
}, },
); );

View file

@ -0,0 +1,46 @@
#overview-head
{
padding-bottom: 12px;
margin-bottom: 12px;
border-bottom: 1px solid;
}
#overview-hint
{
text-align: center;
font-weight: bold;
}
#overview-hint.overview-hint-hidden
{
display: none;
}
#overview-body
{
display: flex;
flex-direction: row;
flex-wrap: nowrap;
}
#overview-body #overview-pane-left
{
flex-grow: 0;
flex-shrink: 1;
}
#overview-body #overview-pane-right
{
flex-grow: 1;
flex-shrink: 1;
}
#overview.overview-compact #overview-pane-left {/*flex-basis: 25%;*/display: none;}
#overview.overview-compact #overview-pane-right {flex-basis: 75%;}
#overview.overview-compact #overview-pane-right-listview {}
#overview.overview-compact #overview-pane-right-weekview {display: none;}
#overview:not(.overview-compact) #overview-pane-left {flex-basis: 12.5%;}
#overview:not(.overview-compact) #overview-pane-right {flex-basis: 87.5%;}
#overview:not(.overview-compact) #overview-pane-right-listview {display: none;}
#overview:not(.overview-compact) #overview-pane-right-weekview {}

View file

@ -1,4 +1,9 @@
.plankton_input_group_field[rel="resource_kind"] .plankton_input_group_field[rel="resource"]
{ {
display: none; display: none;
} }
.weekview-control-count
{
display: none !important;
}

View file

@ -99,11 +99,6 @@ a:hover
transition: 1s ease color; transition: 1s ease color;
} }
input,select,textarea
{
padding: 4px;
}
button button
{ {
padding: 8px; padding: 8px;
@ -122,6 +117,10 @@ input,select,textarea
background-color: hsl(0, 0%, 25%); background-color: hsl(0, 0%, 25%);
border: 1px solid hsl(0, 0%, 25%); border: 1px solid hsl(0, 0%, 25%);
color: hsl(0, 0%, 100%); color: hsl(0, 0%, 100%);
padding: 4px;
margin: 4px; margin: 4px;
border-radius: 4px; border-radius: 4px;
/*
font-family: monospace;
*/
} }

View file

@ -1,18 +1,6 @@
namespace _dali.widgets.listview namespace _dali.widgets.listview
{ {
/**
*/
type type_entry = {
calendar_id : _dali.type_calendar_id;
calendar_name : string;
hue : float;
access_level : _dali.enum_access_level;
event_id : (null | _dali.type_local_resource_event_id);
event_object : _dali.type_event_object;
};
/** /**
*/ */
type type_get_entries = ( type type_get_entries = (
@ -22,7 +10,7 @@ namespace _dali.widgets.listview
calendar_ids : Array<_dali.type_calendar_id> calendar_ids : Array<_dali.type_calendar_id>
) )
=> =>
Promise<Array<type_entry>> Promise<Array<_dali.type_event_object_extended>>
); );
@ -32,22 +20,17 @@ namespace _dali.widgets.listview
{ {
/** /**
* [dependency]
*/ */
private get_entries : type_get_entries; private get_entries : type_get_entries;
/** /**
* [hook]
*/ */
private container : (null | Element); private action_select : (
/**
*/
private action_select_event : (
( (
calendar_id : _dali.type_calendar_id, event_key : _dali.type_event_key
access_level : _dali.enum_access_level,
event_id : _dali.type_local_resource_event_id
) )
=> =>
void void
@ -55,6 +38,7 @@ namespace _dali.widgets.listview
/** /**
* [hook]
*/ */
private action_add : ( private action_add : (
( (
@ -64,16 +48,32 @@ namespace _dali.widgets.listview
); );
/**
*/
private include_passed : boolean;
/**
* [state]
*/
private container : (null | Element);
/** /**
*/ */
public constructor( public constructor(
get_entries : type_get_entries, get_entries : type_get_entries,
options : { {
action_select_event ?: ( "include_passed": include_passed = false,
"action_select": action_select = ((event_key) => {}),
"action_add": action_add = (() => {}),
}
:
{
include_passed ?: boolean;
action_select ?: (
( (
calendar_id : _dali.type_calendar_id, event_key : _dali.type_event_key
access_level : _dali.enum_access_level,
event_id : _dali.type_local_resource_event_id
) )
=> =>
void void
@ -84,39 +84,57 @@ namespace _dali.widgets.listview
=> =>
void void
); );
} = {} }
=
{
}
) )
{ {
options = Object.assign( // dependencies
{
"action_select_event": (calendar_id, access_level, event_id) => {},
"action_select_add": () => {},
},
options
);
this.get_entries = get_entries; this.get_entries = get_entries;
// hooks
this.action_select = action_select;
this.action_add = action_add;
// state
this.include_passed = include_passed;
this.container = null; this.container = null;
this.action_select_event = options.action_select_event;
this.action_add = options.action_add;
} }
/** /**
*/ */
public toggle_visibility( public toggle_visibility(
calendar_id : _dali.type_calendar_id calendar_id : _dali.type_calendar_id,
) : void {
"mode": mode = null,
}
:
{
mode ?: (null | boolean);
}
=
{
}
)
: void
{ {
this.container.querySelectorAll(".listview-entry").forEach( this.container.querySelectorAll(".listview-entry").forEach(
(element) => { (element) => {
const rel : string = element.getAttribute("rel"); const rel : string = element.getAttribute("rel");
const parts : Array<string> = rel.split("/"); const parts : Array<string> = rel.split("/");
const calendar_id_ : _dali.type_calendar_id = parseInt(parts[0]); const calendar_id_ : _dali.type_calendar_id = parseInt(parts[0]);
if (! (calendar_id === calendar_id_)) { if (! (calendar_id === calendar_id_))
{
// do nothing // do nothing
} }
else { else
element.classList.toggle("listview-entry-hidden"); {
element.classList.toggle(
"listview-entry-hidden",
((mode !== null) ? (! mode) : undefined)
);
} }
} }
); );
@ -128,12 +146,13 @@ namespace _dali.widgets.listview
*/ */
public async load( public async load(
target_element : Element target_element : Element
) : Promise<void> )
: Promise<void>
{ {
const now_pit : lib_plankton.pit.type_pit = lib_plankton.pit.now(); const now_pit : lib_plankton.pit.type_pit = lib_plankton.pit.now();
const from_pit : lib_plankton.pit.type_pit = now_pit; const from_pit : lib_plankton.pit.type_pit = now_pit;
const to_pit : lib_plankton.pit.type_pit = lib_plankton.pit.shift_week(now_pit, +4); const to_pit : lib_plankton.pit.type_pit = lib_plankton.pit.shift_week(now_pit, +4);
const entries : Array<type_entry> = await this.get_entries( const entries : Array<_dali.type_event_object_extended> = await this.get_entries(
from_pit, from_pit,
to_pit, to_pit,
null null
@ -165,6 +184,18 @@ namespace _dali.widgets.listview
( (
await _dali.helpers.promise_row<string>( await _dali.helpers.promise_row<string>(
entries entries
.filter(
(entry) => (
this.include_passed
?
true
:
lib_plankton.pit.is_after(
lib_plankton.pit.from_datetime(entry.event_object.begin),
now_pit
)
)
)
.map( .map(
(entry) => () => _dali.helpers.template_coin( (entry) => () => _dali.helpers.template_coin(
"widget-listview", "widget-listview",
@ -228,36 +259,8 @@ namespace _dali.widgets.listview
entry.event_object.description entry.event_object.description
), ),
"raw": JSON.stringify(entry), "raw": JSON.stringify(entry),
"color": lib_plankton.color.output_hex( "color": _dali.helpers.event_color(entry.hue),
lib_plankton.color.make_hsv( "rel": entry.key,
{
"hue": entry.hue,
"saturation": 0.375,
"value": 0.375,
}
),
),
"rel": lib_plankton.string.coin(
"{{calendar_id}}/{{event_id}}/{{access_level}}",
{
"calendar_id": entry.calendar_id.toFixed(0),
"event_id": (
(! (entry.event_id === null))
?
entry.event_id.toFixed(0)
:
"-"
),
"access_level": (() => {
switch (entry.access_level) {
case _dali.enum_access_level.none: return "none";
case _dali.enum_access_level.view: return "view";
case _dali.enum_access_level.edit: return "edit";
case _dali.enum_access_level.admin: return "admin";
}
}) (),
}
),
}, },
) )
) )
@ -283,33 +286,14 @@ namespace _dali.widgets.listview
element.addEventListener( element.addEventListener(
"click", "click",
(event) => { (event) => {
if ((event.target as Element).nodeName === "A") { if ((event.target as Element).nodeName === "A")
{
// do nothing // do nothing
} }
else { else
const rel : string = element.getAttribute("rel"); {
const parts : Array<string> = rel.split("/"); const event_key : string = element.getAttribute("rel");
const calendar_id : _dali.type_calendar_id = parseInt(parts[0]); this.action_select(event_key);
const event_id : (null | _dali.type_local_resource_event_id) = (
parts[1] === "-"
?
null
:
parseInt(parts[1])
);
const access_level : _dali.enum_access_level = (() => {
switch (parts[2]) {
case "none": return _dali.enum_access_level.none;
case "view": return _dali.enum_access_level.view;
case "edit": return _dali.enum_access_level.edit;
case "admin": return _dali.enum_access_level.admin;
}
}) ();
this.action_select_event(
calendar_id,
access_level,
event_id,
);
} }
} }
); );

View file

@ -0,0 +1,60 @@
.listview-add
{
margin-left: 12px;
}
.listview-add-hidden
{
display: none;
}
.listview-entries
{
padding: 0;
margin: 0;
list-style-type: none;
}
.listview-entry
{
background: hsl(0,0%,25%);
padding: 12px;
border-radius: 4px;
margin: 12px;
cursor: pointer;
}
.listview-entry-hidden
{
display: none;
}
.listview-entry-name
{
font-weight: bold;
font-size: 1.25em;
}
.listview-entry-label
{
font-weight: bold;
}
.listview-entry-label::before
{
content: "[";
}
.listview-entry-label::after
{
content: "] ";
}
.listview-entry-field-empty {
display: none;
}
.listview-entry-field:not(:last-child)
{
margin-bottom: 16px;
}

View file

@ -0,0 +1,74 @@
.widget-menu
{
margin-left: auto;
margin-right: 8px;
width: fit-content;
}
.widget-menu.widget-menu-collapsed > .widget-menu-platform
{
display: none;
}
.widget-menu-button
{
text-transform: initial;
}
.widget-menu-entry
{
cursor: pointer;
}
.widget-menu-platform
{
background-color: hsl(var(--hue), 0%, 25%);
border-radius: 2px;
border: 1px solid hsl(var(--hue), 0%, 0%);
padding: 8px;
min-width: 200px;
}
.widget-menu-platform:not(.widget-menu-platform-collapsed)
{
position: fixed;
top: 50px;
right: 20px;
z-index: 2;
}
.widget-menu-entries
{
padding: 0;
margin: 0;
list-style-type: none;
}
.widget-menu-entry
{
margin: 8px;
text-transform: capitalize;
}
.widget-menu-entry:not(:hover)
{
background-color: hsl(var(--hue), 0%, 25%);
color: hsl(var(--hue), 0%, 100%);
}
.widget-menu-entry:hover::before
{
content: "» ";
/*
background-color: hsl(var(--hue), 0%, 50%);
color: hsl(var(--hue), 0%, 100%);
*/
}
.widget-menu-entry.widget-menu-entry-hidden
{
display: none;
}

View file

@ -31,7 +31,7 @@ namespace _dali.widgets.sources
/** /**
* [hook] * [hook]
*/ */
private action_toggle_visibility : ((entry : type_entry) => void); private action_toggle_visibility : ((entry : type_entry, mode : boolean) => void);
/** /**
@ -41,6 +41,7 @@ namespace _dali.widgets.sources
/** /**
* [state]
*/ */
private priviliged : boolean; private priviliged : boolean;
@ -57,14 +58,14 @@ namespace _dali.widgets.sources
get_entries : (() => Promise<Array<type_entry>>), get_entries : (() => Promise<Array<type_entry>>),
{ {
"action_open": action_open = ((calendar_id) => {}), "action_open": action_open = ((calendar_id) => {}),
"action_toggle_visibility": action_toggle_visibility = ((calendar_id) => {}), "action_toggle_visibility": action_toggle_visibility = ((calendar_id, mode) => {}),
"action_create": action_create = (() => {}), "action_create": action_create = (() => {}),
"initial_priviliged": initial_priviliged = false, "initial_priviliged": initial_priviliged = false,
} }
: :
{ {
action_open ?: ((entry : type_entry) => void); action_open ?: ((entry : type_entry) => void);
action_toggle_visibility ?: ((entry : type_entry) => void); action_toggle_visibility ?: ((entry : type_entry, mode : boolean) => void);
action_create ?: (() => void); action_create ?: (() => void);
initial_priviliged ?: boolean; initial_priviliged ?: boolean;
} }
@ -169,46 +170,8 @@ namespace _dali.widgets.sources
"entry", "entry",
{ {
"name": pair.value.name, "name": pair.value.name,
"label_toggle": lib_plankton.string.coin(
"{{show}}/{{hide}}",
{
"show": lib_plankton.translate.get("common.show"),
"hide": lib_plankton.translate.get("common.hide"),
}
),
"label_edit": lib_plankton.translate.get("common.edit"),
// "access_level": entry.access_level, // TODO // "access_level": entry.access_level, // TODO
// TODO centralize "color": _dali.helpers.event_color(pair.value.hue),
"color_head": lib_plankton.color.output_hex(
lib_plankton.color.make_hsv(
{
"hue": pair.value.hue,
/**
* @todo const and outsource
*/
"saturation": 0.375,
/**
* @todo const and outsource
*/
"value": 0.375,
}
),
),
"color_body": lib_plankton.color.output_hex(
lib_plankton.color.make_hsv(
{
"hue": pair.value.hue,
/**
* @todo const and outsource
*/
"saturation": 0.375,
/**
* @todo const and outsource
*/
"value": 0.25,
}
),
),
"rel": class_widget_sources.id_encode(pair.key), "rel": class_widget_sources.id_encode(pair.key),
} }
); );
@ -231,40 +194,37 @@ namespace _dali.widgets.sources
this.action_create(); this.action_create();
} }
); );
this.container.querySelectorAll(".sources-entry-head").forEach( this.container.querySelectorAll(".sources-entry-visibility").forEach(
(element) => { (element) => {
element.addEventListener( element.addEventListener(
"click", "change",
(event) => {
element.parentElement.classList.toggle("sources-entry-open");
}
);
}
);
this.container.querySelectorAll(".sources-entry-toggle").forEach(
(element) => {
element.addEventListener(
"click",
() => { () => {
const key_encoded : string = element.parentElement.parentElement.parentElement.getAttribute("rel"); const mode : boolean = (element as HTMLInputElement).checked;
const key_encoded : string = element.parentElement.getAttribute("rel");
const calendar_id : _dali.type_calendar_id = class_widget_sources.id_decode(key_encoded); const calendar_id : _dali.type_calendar_id = class_widget_sources.id_decode(key_encoded);
const entry : type_entry = data.get(calendar_id); const entry : type_entry = data.get(calendar_id);
element.parentElement.parentElement.parentElement.classList.toggle("sources-entry-hidden"); element.parentElement.classList.toggle("sources-entry-hidden", (! mode));
element.parentElement.parentElement.parentElement.classList.toggle("sources-entry-open", false); this.action_toggle_visibility(entry, mode);
this.action_toggle_visibility(entry);
} }
); );
} }
); );
this.container.querySelectorAll(".sources-entry-edit").forEach( this.container.querySelectorAll(".sources-entry").forEach(
(element) => { (element) => {
element.addEventListener( element.addEventListener(
"click", "click",
(event) => { (event) => {
const key_encoded : string = element.parentElement.parentElement.parentElement.getAttribute("rel"); if ((event.target as Element).classList.contains("sources-entry-visibility"))
const calendar_id : _dali.type_calendar_id = class_widget_sources.id_decode(key_encoded); {
const entry : type_entry = data.get(calendar_id); // do nothing
this.action_open(entry); }
else
{
const key_encoded : string = element.getAttribute("rel");
const calendar_id : _dali.type_calendar_id = class_widget_sources.id_decode(key_encoded);
const entry : type_entry = data.get(calendar_id);
this.action_open(entry);
}
} }
); );
} }

View file

@ -0,0 +1,36 @@
.sources
{
font-size: 0.75em;
}
.sources:not(.sources-priviliged) > .sources-create
{
display: none;
}
.sources-create
{
margin-left: 8px;
}
.sources-entries
{
margin: 0;
padding: 0;
list-style-type: none;
}
.sources-entry
{
margin: 8px;
padding: 4px;
cursor: pointer;
}
/*
.sources-entry-hidden
{
filter: saturate(0);
}
*/

View file

@ -1,11 +1,4 @@
<li class="sources-entry" style="background-color: {{color_head}}" rel="{{rel}}"> <li class="sources-entry" style="background-color: {{color}}" rel="{{rel}}">
<div class="sources-entry-head"> <input class="sources-entry-visibility" type="checkbox" checked="checked"/>
<span>{{name}}</span> <span class="sources-entry-name">{{name}}</span>
</div>
<div class="sources-entry-body" style="background-color: {{color_body}}">
<ul>
<li class="sources-entry-action sources-entry-toggle">{{label_toggle}}</li>
<li class="sources-entry-action sources-entry-edit">{{label_edit}}</li>
</ul>
</div>
</li> </li>

View file

@ -1,19 +1,6 @@
namespace _dali.widgets.weekview namespace _dali.widgets.weekview
{ {
/**
*/
type type_entry = {
key : _dali.type_event_key;
calendar_id : _dali.type_calendar_id;
calendar_name : string;
hue : float;
access_level : _dali.enum_access_level;
event_id : (null | _dali.type_local_resource_event_id);
event_object : _dali.type_event_object;
};
/** /**
*/ */
type type_get_entries = ( type type_get_entries = (
@ -23,7 +10,7 @@ namespace _dali.widgets.weekview
calendar_ids : Array<_dali.type_calendar_id> calendar_ids : Array<_dali.type_calendar_id>
) )
=> =>
Promise<Array<type_entry>> Promise<Array<_dali.type_event_object_extended>>
); );
@ -62,6 +49,12 @@ namespace _dali.widgets.weekview
); );
/**
* [state]
*/
private vertical : boolean;
/** /**
* [state] * [state]
*/ */
@ -105,9 +98,10 @@ namespace _dali.widgets.weekview
{ {
"action_select_day": action_select_day = ((date) => {}), "action_select_day": action_select_day = ((date) => {}),
"action_select_event": action_select_event = ((event_key) => {}), "action_select_event": action_select_event = ((event_key) => {}),
"vertical": vertical = true,
"initial_year": initial_year = null, "initial_year": initial_year = null,
"initial_week": initial_week = null, "initial_week": initial_week = null,
"initial_count": initial_count = 5, "initial_count": initial_count = 4,
} }
: :
{ {
@ -125,6 +119,7 @@ namespace _dali.widgets.weekview
=> =>
void void
); );
vertical ?: boolean;
initial_year ?: (null | int); initial_year ?: (null | int);
initial_week ?: (null | int); initial_week ?: (null | int);
initial_count ?: int; initial_count ?: int;
@ -142,6 +137,7 @@ namespace _dali.widgets.weekview
// state // state
const ywd_now : lib_plankton.pit.type_ywd = lib_plankton.pit.to_ywd(lib_plankton.pit.now()); const ywd_now : lib_plankton.pit.type_ywd = lib_plankton.pit.to_ywd(lib_plankton.pit.now());
this.vertical = vertical;
this.year = ( this.year = (
initial_year initial_year
?? ??
@ -174,7 +170,7 @@ namespace _dali.widgets.weekview
* @todo sha256 hash? * @todo sha256 hash?
*/ */
private static entry_hash( private static entry_hash(
entry : type_entry entry : _dali.type_event_object_extended
) : string ) : string
{ {
return lib_plankton.call.convey( return lib_plankton.call.convey(
@ -303,7 +299,7 @@ namespace _dali.widgets.weekview
{ {
} }
) )
: Promise<Array<type_entry>> : Promise<Array<_dali.type_event_object_extended>>
{ {
const entries = await this.get_entries( const entries = await this.get_entries(
lib_plankton.pit.from_ywd( lib_plankton.pit.from_ywd(
@ -342,7 +338,7 @@ namespace _dali.widgets.weekview
/** /**
*/ */
private async entry_insert( private async entry_insert(
entry : type_entry entry : _dali.type_event_object_extended
) : Promise<(null | HTMLElement)> ) : Promise<(null | HTMLElement)>
{ {
const selector : string = lib_plankton.string.coin( const selector : string = lib_plankton.string.coin(
@ -369,21 +365,7 @@ namespace _dali.widgets.weekview
"widget-weekview", "widget-weekview",
"tableview-cell-entry", "tableview-cell-entry",
{ {
"color": lib_plankton.color.output_hex( "color": _dali.helpers.event_color(entry.hue),
lib_plankton.color.make_hsv(
{
"hue": entry.hue,
/**
* @todo as constant
*/
"saturation": 0.375,
/**
* @todo as constant
*/
"value": 0.375,
}
)
),
"title": class_widget_weekview.event_generate_tooltip( "title": class_widget_weekview.event_generate_tooltip(
entry.calendar_name, entry.calendar_name,
entry.event_object entry.event_object
@ -423,7 +405,7 @@ namespace _dali.widgets.weekview
/** /**
*/ */
private async entry_add( private async entry_add(
entry : type_entry entry : _dali.type_event_object_extended
) : Promise<void> ) : Promise<void>
{ {
const dom_entry : (null | HTMLElement) = await this.entry_insert(entry); const dom_entry : (null | HTMLElement) = await this.entry_insert(entry);
@ -448,7 +430,7 @@ namespace _dali.widgets.weekview
*/ */
private async entry_update( private async entry_update(
key : _dali.type_event_key, key : _dali.type_event_key,
entry : type_entry entry : _dali.type_event_object_extended
) : Promise<void> ) : Promise<void>
{ {
if (! this.event_map.has(key)) if (! this.event_map.has(key))
@ -536,12 +518,12 @@ namespace _dali.widgets.weekview
public async update_entries( public async update_entries(
) : Promise<void> ) : Promise<void>
{ {
const entries : Array<type_entry> = await this.get_entries_wrapped( const entries : Array<_dali.type_event_object_extended> = await this.get_entries_wrapped(
); );
const contrast = lib_plankton.list.contrast< const contrast = lib_plankton.list.contrast<
any, any,
type_entry _dali.type_event_object_extended
>( >(
lib_plankton.map.dump(this.event_map), lib_plankton.map.dump(this.event_map),
pair => pair.key, pair => pair.key,
@ -578,9 +560,10 @@ namespace _dali.widgets.weekview
) : Promise<void> ) : Promise<void>
{ {
const context : Element = this.container; const context : Element = this.container;
(context.querySelector(".weekview-control-year > input") as HTMLInputElement).value = this.year.toFixed(0); (context.querySelector(".weekview-control-year input") as HTMLInputElement).value = this.year.toFixed(0);
(context.querySelector(".weekview-control-week > input") as HTMLInputElement).value = this.week.toFixed(0); (context.querySelector(".weekview-control-week input") as HTMLInputElement).value = this.week.toFixed(0);
(context.querySelector(".weekview-control-count > input") as HTMLInputElement).value = this.count.toFixed(0); (context.querySelector(".weekview-control-count input") as HTMLInputElement).value = this.count.toFixed(0);
(context.querySelector(".weekview-control-vertical input") as HTMLInputElement).checked = this.vertical;
} }
@ -596,6 +579,13 @@ namespace _dali.widgets.weekview
const context : Element = this.container; const context : Element = this.container;
// structure // structure
{ {
type type_row_data = Array<
{
week : int;
day_pits : Array<lib_plankton.pit.type_pit>;
}
>;
/** /**
* @todo als Variable? * @todo als Variable?
*/ */
@ -603,127 +593,217 @@ namespace _dali.widgets.weekview
const now_pit : lib_plankton.pit.type_pit = lib_plankton.pit.now(); const now_pit : lib_plankton.pit.type_pit = lib_plankton.pit.now();
const today_begin_pit : lib_plankton.pit.type_pit = lib_plankton.pit.trunc_day(now_pit); const today_begin_pit : lib_plankton.pit.type_pit = lib_plankton.pit.trunc_day(now_pit);
const today_end_pit : lib_plankton.pit.type_pit = lib_plankton.pit.shift_day(today_begin_pit, 1); const today_end_pit : lib_plankton.pit.type_pit = lib_plankton.pit.shift_day(today_begin_pit, 1);
const row_data : Array< const day_names : Array<string> = [
{ lib_plankton.translate.get("common.weekday.monday"),
week : int; lib_plankton.translate.get("common.weekday.tuesday"),
data : Array< lib_plankton.translate.get("common.weekday.wednesday"),
{ lib_plankton.translate.get("common.weekday.thursday"),
pit : lib_plankton.pit.type_pit; lib_plankton.translate.get("common.weekday.friday"),
today : boolean; lib_plankton.translate.get("common.weekday.saturday"),
} lib_plankton.translate.get("common.weekday.sunday"),
>; ];
} const row_data_original : type_row_data = (
> = (
lib_plankton.list.sequence(this.count) lib_plankton.list.sequence(this.count)
.map( .map(
offset => { offset => {
const week : int = (this.week + offset); const week : int = (this.week + offset);
return { return {
"week": week, "week": week,
"data": ( "day_pits": (
lib_plankton.list.sequence(7) lib_plankton.list.sequence(7)
.map( .map(
day => { day => lib_plankton.pit.from_ywd(
const day_pit : lib_plankton.pit.type_pit = lib_plankton.pit.from_ywd( {
{ "year": this.year,
"year": this.year, "week": week,
"week": week, "day": (1 + day),
"day": (day + 1), },
}, {
{ "timezone_shift": timezone_shift,
"timezone_shift": timezone_shift, }
} )
);
return {
"pit": day_pit,
"today": lib_plankton.pit.is_between(
day_pit,
today_begin_pit,
today_end_pit
),
};
}
) )
), ),
}; };
} }
) )
); );
context.querySelector(".weekview-table tbody").innerHTML = ( const row_data_alternative : type_row_data = (
await _dali.helpers.promise_row<string>( lib_plankton.list.sequence(7)
row_data .map(
.map( day_of_week => {
(row) => async () => _dali.helpers.template_coin( return {
"widget-weekview", /*"day_of_week"*/"week": day_of_week,
"tableview-row", /*"week_pits"*/"day_pits": (
{ lib_plankton.list.sequence(this.count)
"week": row.week.toFixed(0).padStart(2, "0"), .map(
"cells": ( offset => {
await _dali.helpers.promise_row<string>( const week : int = (this.week + offset);
row.data return lib_plankton.pit.from_ywd(
.map( {
(cell) => async () => _dali.helpers.template_coin( "year": this.year,
"widget-weekview", "week": week,
"tableview-cell", "day": (1 + day_of_week),
{ },
"extra_classes": ( {
[""] "timezone_shift": timezone_shift,
.concat(cell.today ? ["weekview-cell-today"] : []) }
.join(" ") );
), }
"title": lib_plankton.call.convey( )
cell.pit, )
[ };
lib_plankton.pit.to_datetime_ce, }
(x : lib_plankton.pit.type_datetime) => lib_plankton.string.coin( )
"{{year}}-{{month}}-{{day}}", );
{ const row_data : type_row_data = (
"year": x.date.year.toFixed(0).padStart(4, "0"), (! this.vertical)
"month": x.date.month.toFixed(0).padStart(2, "0"), ?
"day": x.date.day.toFixed(0).padStart(2, "0"), row_data_original
} :
row_data_alternative
);
// head
{
const dom_tr = document.createElement("tr");
{
if (! this.vertical)
{
// anchor
{
const dom_th = document.createElement("th");
dom_th.classList.add("weekview-cell");
dom_tr.appendChild(dom_th);
}
// days
{
day_names.forEach(
(day_name) => {
const dom_th = document.createElement("th");
dom_th.classList.add("weekview-cell");
dom_th.classList.add("weekview-cell-week");
dom_th.textContent = day_name;
dom_tr.appendChild(dom_th);
}
);
}
}
else
{
// anchor
{
const dom_th = document.createElement("th");
dom_th.classList.add("weekview-cell");
dom_tr.appendChild(dom_th);
}
// days
{
lib_plankton.list.sequence(this.count).forEach(
(offset) => {
const dom_th = document.createElement("th");
dom_th.classList.add("weekview-cell");
dom_th.classList.add("weekview-cell-day");
dom_th.textContent = (this.week + offset).toFixed(0).padStart(2, "0");
dom_tr.appendChild(dom_th);
}
);
}
}
}
context.querySelector(".weekview-table thead").innerHTML = "";
context.querySelector(".weekview-table thead").appendChild(dom_tr);
}
// body
{
context.querySelector(".weekview-table tbody").innerHTML = (
await _dali.helpers.promise_row<string>(
row_data
.map(
(row, index) => async () => _dali.helpers.template_coin(
"widget-weekview",
"tableview-row",
{
"week": (
(! this.vertical)
?
row.week.toFixed(0).padStart(2, "0")
:
day_names[index]
),
"cells": (
await _dali.helpers.promise_row<string>(
row.day_pits
.map(
(day_pit) => async () => {
const is_today : boolean = lib_plankton.pit.is_between(
day_pit,
today_begin_pit,
today_end_pit
);
return _dali.helpers.template_coin(
"widget-weekview",
"tableview-cell",
{
"extra_classes": (
[""]
.concat(is_today ? ["weekview-cell-today"] : [])
.join(" ")
), ),
] "title": lib_plankton.call.convey(
), day_pit,
"day": lib_plankton.call.convey( [
cell.pit, lib_plankton.pit.to_datetime_ce,
[ (x : lib_plankton.pit.type_datetime) => lib_plankton.string.coin(
lib_plankton.pit.to_datetime_ce, "{{year}}-{{month}}-{{day}}",
(x : lib_plankton.pit.type_datetime) => lib_plankton.string.coin( {
"{{day}}", "year": x.date.year.toFixed(0).padStart(4, "0"),
{ "month": x.date.month.toFixed(0).padStart(2, "0"),
"year": x.date.year.toFixed(0).padStart(4, "0"), "day": x.date.day.toFixed(0).padStart(2, "0"),
"month": x.date.month.toFixed(0).padStart(2, "0"), }
"day": x.date.day.toFixed(0).padStart(2, "0"), ),
} ]
), ),
] "day": lib_plankton.call.convey(
), day_pit,
"rel": lib_plankton.call.convey( [
cell.pit, lib_plankton.pit.to_datetime_ce,
[ (x : lib_plankton.pit.type_datetime) => lib_plankton.string.coin(
lib_plankton.pit.to_datetime_ce, "{{day}}",
(x : lib_plankton.pit.type_datetime) => lib_plankton.string.coin( {
"{{year}}-{{month}}-{{day}}", "year": x.date.year.toFixed(0).padStart(4, "0"),
{ "month": x.date.month.toFixed(0).padStart(2, "0"),
"year": x.date.year.toFixed(0).padStart(4, "0"), "day": x.date.day.toFixed(0).padStart(2, "0"),
"month": x.date.month.toFixed(0).padStart(2, "0"), }
"day": x.date.day.toFixed(0).padStart(2, "0"), ),
} ]
) ),
] "rel": lib_plankton.call.convey(
), day_pit,
"entries": "" [
lib_plankton.pit.to_datetime_ce,
(x : lib_plankton.pit.type_datetime) => lib_plankton.string.coin(
"{{year}}-{{month}}-{{day}}",
{
"year": x.date.year.toFixed(0).padStart(4, "0"),
"month": x.date.month.toFixed(0).padStart(2, "0"),
"day": x.date.day.toFixed(0).padStart(2, "0"),
}
)
]
),
"entries": ""
}
);
} }
) )
) )
) ).join(""),
).join(""), }
} )
) )
) )
) ).join("");
).join(""); }
} }
// listeners // listeners
{ {
@ -782,7 +862,10 @@ namespace _dali.widgets.weekview
} }
else else
{ {
element.classList.toggle("weekview-cell-hidden", mode ?? undefined); element.classList.toggle(
"weekview-cell-hidden",
((mode !== null) ? (! mode) : undefined)
);
} }
} }
); );
@ -803,51 +886,164 @@ namespace _dali.widgets.weekview
"label_control_year": lib_plankton.translate.get("widget.weekview.controls.year"), "label_control_year": lib_plankton.translate.get("widget.weekview.controls.year"),
"label_control_week": lib_plankton.translate.get("widget.weekview.controls.week"), "label_control_week": lib_plankton.translate.get("widget.weekview.controls.week"),
"label_control_count": lib_plankton.translate.get("widget.weekview.controls.count"), "label_control_count": lib_plankton.translate.get("widget.weekview.controls.count"),
"label_control_vertical": lib_plankton.translate.get("widget.weekview.controls.vertical"),
"label_control_apply": lib_plankton.translate.get("widget.weekview.controls.apply"), "label_control_apply": lib_plankton.translate.get("widget.weekview.controls.apply"),
"label_weekday_monday": lib_plankton.translate.get("common.weekday.monday"),
"label_weekday_tuesday": lib_plankton.translate.get("common.weekday.tuesday"),
"label_weekday_wednesday": lib_plankton.translate.get("common.weekday.wednesday"),
"label_weekday_thursday": lib_plankton.translate.get("common.weekday.thursday"),
"label_weekday_friday": lib_plankton.translate.get("common.weekday.friday"),
"label_weekday_saturday": lib_plankton.translate.get("common.weekday.saturday"),
"label_weekday_sunday": lib_plankton.translate.get("common.weekday.sunday"),
} }
); );
this.container = target_element.querySelector(".weekview"); this.container = target_element.querySelector(".weekview");
// controls // controls
{ {
[ // direct inputs
{
[
{
"name": "year",
"selector": ".weekview-control-year input",
"retrieve": element => parseInt(element.value),
"write": x => {this.year = x;}
},
{
"name": "week",
"selector": ".weekview-control-week input",
"retrieve": element => parseInt(element.value),
"write": x => {this.week = x;}
},
{
"name": "count",
"selector": ".weekview-control-count input",
"retrieve": element => parseInt(element.value),
"write": x => {this.count = x;}
},
{
"name": "vertical",
"selector": ".weekview-control-vertical input",
"retrieve": element => element.checked,
"write": x => {this.vertical = x;}
},
].forEach(
(entry) => {
const element : HTMLInputElement = (target_element.querySelector(entry.selector) as HTMLInputElement);
element.addEventListener(
"change",
async (event) => {
event.preventDefault();
const value : unknown = entry.retrieve(element);
entry.write(value);
await this.update_table();
await this.update_entries();
}
);
}
);
}
// buttons
{
// year
{ {
"name": "year", /**
"transform": parseInt, * @todo limit
"write": x => {this.year = x;} */
}, target_element.querySelector(".weekview-control-year-prev").addEventListener(
{ "click",
"name": "week", async () => {
"transform": parseInt, this.year -= 1;
"write": x => {this.week = x;} await this.update_controls();
}, await this.update_table();
{ await this.update_entries();
"name": "count", }
"transform": parseInt, );
"write": x => {this.count = x;} /**
}, * @todo limit
].forEach( */
(entry) => { target_element.querySelector(".weekview-control-year-next").addEventListener(
const selector : string = (".weekview-control-" + entry.name + " > input"); "click",
const element : HTMLInputElement = (target_element.querySelector(selector) as HTMLInputElement); async () => {
element.addEventListener( this.year += 1;
"change", await this.update_controls();
async (event) => {
event.preventDefault();
const value : int = entry.transform(element.value);
entry.write(value);
await this.update_table(); await this.update_table();
await this.update_entries(); await this.update_entries();
} }
); );
} }
); // week
{
target_element.querySelector(".weekview-control-week-prev").addEventListener(
"click",
async () => {
if (this.week >= 1)
{
this.week -= 1;
}
else
{
this.year -= 1;
/**
* @todo get correct week
*/
this.week = 51;
}
await this.update_controls();
await this.update_table();
await this.update_entries();
}
);
target_element.querySelector(".weekview-control-week-next").addEventListener(
"click",
async () => {
/**
* @todo correct limit
*/
if (this.week <= 51)
{
this.week += 1;
}
else
{
this.year += 1;
this.week = 1;
}
await this.update_controls();
await this.update_table();
await this.update_entries();
}
);
}
// count
{
target_element.querySelector(".weekview-control-count-prev").addEventListener(
"click",
async () => {
if (this.count >= 2)
{
this.count -= 1;
await this.update_controls();
await this.update_table();
await this.update_entries();
}
else
{
// do nothing
}
}
);
target_element.querySelector(".weekview-control-count-next").addEventListener(
"click",
async () => {
if (this.count <= 6)
{
this.count += 1;
await this.update_controls();
await this.update_table();
await this.update_entries();
}
else
{
// do nothing
}
}
);
}
}
} }
await this.update_controls(); await this.update_controls();
await this.update_table(); await this.update_table();

View file

@ -0,0 +1,131 @@
.weekview-controls
{
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: right;
margin-bottom: 12px;
text-align: center;
}
.weekview-control
{
flex-basis: 0;
flex-grow: 0;
flex-shrink: 1;
min-width: 140px;
margin: 0 12px;
}
.weekview-control-label
{
display: block;
font-size: 0.75em;
/*
text-transform: uppercase;
*/
}
.weekview-control-input > *
{
display: inline-block;
}
.weekview-control input
{
max-width: 40px;
}
.weekview-table table
{
width: 100%;
border-collapse: collapse;
}
.weekview-cell
{
border: 1px solid hsl(0,0%,37.5%);
padding: 8px;
vertical-align: top;
}
.weekview-cell-day
{
/* todo */
width: 13.5%;
}
.weekview-cell-week
{
/* todo */
width: 5.5%;
}
.weekview-cell-regular
{
/* todo */
width: 13.5%;
height: 100px;
cursor: copy;
}
.weekview-cell-today
{
outline: 2px solid hsl(0, 0%, 100%);
}
.weekview-cell-hidden
{
display: none;
}
.weekview-day
{
font-size: 0.75em;
cursor: help;
}
.weekview-events
{
margin: 0;
padding: 0;
list-style-type: none;
}
.weekview-event_entry
{
margin: 4px;
padding: 4px;
border-radius: 2px;
font-size: 0.75em;
color: #FFF;
font-weight: bold;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
min-width: 75px;
word-wrap: anywhere;
}
.weekview-event_entry.access_level-none
,
.weekview-event_entry.access_level-view
{
/*
cursor: default;
*/
cursor: pointer;
}
.weekview-event_entry.access_level-edit
,
.weekview-event_entry.access_level-admin
{
cursor: pointer;
}

View file

@ -1,31 +1,39 @@
<div class="weekview"> <div class="weekview">
<div class="weekview-controls"> <div class="weekview-controls">
<label class="weekview-control weekview-control-year"> <div class="weekview-control weekview-control-year">
<span>{{label_control_year}}</span> <label class="weekview-control-label">{{label_control_year}}</label>
<input type="number"/> <div class="weekview-control-input">
</label> <button class="weekview-control-year-prev">&#x25C2;</button>
<label class="weekview-control weekview-control-week"> <input type="text"/>
<span>{{label_control_week}}</span> <button class="weekview-control-year-next">&#x25B8;</button>
<input type="number"/> </div>
</label> </div>
<label class="weekview-control weekview-control-count"> <div class="weekview-control weekview-control-week">
<span>{{label_control_count}}</span> <label class="weekview-control-label">{{label_control_week}}</label>
<input type="number"/> <div class="weekview-control-input">
</label> <button class="weekview-control-week-prev">&#x25C2;</button>
<input type="text"/>
<button class="weekview-control-week-next">&#x25B8;</button>
</div>
</div>
<div class="weekview-control weekview-control-count">
<label class="weekview-control-label">{{label_control_count}}</label>
<div class="weekview-control-input">
<button class="weekview-control-count-prev">&#x25C2;</button>
<input type="text"/>
<button class="weekview-control-count-next">&#x25B8;</button>
</div>
</div>
<div class="weekview-control weekview-control-vertical">
<label class="weekview-control-label">{{label_control_vertical}}</label>
<div class="weekview-control-input">
<input type="checkbox"/>
</div>
</div>
</div> </div>
<div class="weekview-table"> <div class="weekview-table">
<table> <table>
<thead> <thead>
<tr>
<th class="calendar-cell"></th>
<th class="calendar-cell calendar-cell-day">{{label_weekday_monday}}</th>
<th class="calendar-cell calendar-cell-day">{{label_weekday_tuesday}}</th>
<th class="calendar-cell calendar-cell-day">{{label_weekday_wednesday}}</th>
<th class="calendar-cell calendar-cell-day">{{label_weekday_thursday}}</th>
<th class="calendar-cell calendar-cell-day">{{label_weekday_friday}}</th>
<th class="calendar-cell calendar-cell-day">{{label_weekday_saturday}}</th>
<th class="calendar-cell calendar-cell-day">{{label_weekday_sunday}}</th>
</tr>
</thead> </thead>
<tbody> <tbody>
</tbody> </tbody>

View file

@ -24,16 +24,24 @@ def main():
metavar = "<conf-path>", metavar = "<conf-path>",
help = "conf path", help = "conf path",
) )
argument_parser.add_argument(
"-f",
"--force",
action = 'store_true',
default = False,
help = "force",
)
args = argument_parser.parse_args() args = argument_parser.parse_args()
## exec ## exec
targets = [] targets = []
targets.append("default") targets.append("default")
_os.system( _os.system(
"make dir_build=%s --file=tools/makefile %s" "make dir_build=%s --file=tools/makefile %s%s"
% ( % (
args.output_directory, args.output_directory,
" ".join(targets), " ".join(targets),
(" --always-make" if args.force else ""),
) )
) )
if True: if True:

View file

@ -31,14 +31,6 @@ def main(
dest = "index_template_path", dest = "index_template_path",
type = str, type = str,
) )
argument_parser.add_argument(
"-t",
"--template",
dest = "template_paths",
type = str,
action = "append",
default = []
)
args = argument_parser.parse_args() args = argument_parser.parse_args()
## exec ## exec
@ -46,12 +38,6 @@ def main(
string_coin( string_coin(
file_read(args.index_template_path), file_read(args.index_template_path),
{ {
"templates": "".join(
map(
file_read,
args.template_paths
)
),
} }
) )
) )

View file

@ -26,9 +26,7 @@ ${dir_build}/index.html: \
${dir_source}/index.html.tpl ${dir_source}/index.html.tpl
@ ${cmd_log} "index …" @ ${cmd_log} "index …"
@ ${cmd_mkdir} $(dir $@) @ ${cmd_mkdir} $(dir $@)
@ tools/make-index \ @ tools/make-index ${dir_source}/index.html.tpl > $@
${dir_source}/index.html.tpl \
> $@
.PHONY: templates .PHONY: templates
templates: \ templates: \
@ -37,80 +35,68 @@ templates: \
templates-widgets-listview \ templates-widgets-listview \
templates-widgets-weekview \ templates-widgets-weekview \
templates-widgets-mode_switcher \ templates-widgets-mode_switcher \
templates-widgets-calendar_edit \
templates-widgets-event_edit \
templates-pages-caldav \ templates-pages-caldav \
templates-pages-overview templates-pages-overview
.PHONY: templates-widgets-login .PHONY: templates-widgets-login
templates-widgets-login: \ templates-widgets-login: \
$(wildcard ${dir_source}/widgets/login/templates/*) $(wildcard ${dir_source}/widgets/login/templates/*)
@ ${cmd_log} "templates:widgets:login …" @ ${cmd_log} "templates:widget:login …"
@ ${cmd_mkdir} ${dir_build}/templates/widget-login @ ${cmd_mkdir} ${dir_build}/templates/widget-login
@ ${cmd_cp} -r -u -v ${dir_source}/widgets/login/templates/* ${dir_build}/templates/widget-login/ @ ${cmd_cp} -r -u -v ${dir_source}/widgets/login/templates/* ${dir_build}/templates/widget-login/
.PHONY: templates-widgets-sources .PHONY: templates-widgets-sources
templates-widgets-sources: \ templates-widgets-sources: \
$(wildcard ${dir_source}/widgets/sources/templates/*) $(wildcard ${dir_source}/widgets/sources/templates/*)
@ ${cmd_log} "templates:widgets:sources …" @ ${cmd_log} "templates:widget:sources …"
@ ${cmd_mkdir} ${dir_build}/templates/widget-sources @ ${cmd_mkdir} ${dir_build}/templates/widget-sources
@ ${cmd_cp} -r -u -v ${dir_source}/widgets/sources/templates/* ${dir_build}/templates/widget-sources/ @ ${cmd_cp} -r -u -v ${dir_source}/widgets/sources/templates/* ${dir_build}/templates/widget-sources/
.PHONY: templates-widgets-listview .PHONY: templates-widgets-listview
templates-widgets-listview: \ templates-widgets-listview: \
$(wildcard ${dir_source}/widgets/listview/templates/*) $(wildcard ${dir_source}/widgets/listview/templates/*)
@ ${cmd_log} "templates:widgets:listview …" @ ${cmd_log} "templates:widget:listview …"
@ ${cmd_mkdir} ${dir_build}/templates/widget-listview @ ${cmd_mkdir} ${dir_build}/templates/widget-listview
@ ${cmd_cp} -r -u -v ${dir_source}/widgets/listview/templates/* ${dir_build}/templates/widget-listview/ @ ${cmd_cp} -r -u -v ${dir_source}/widgets/listview/templates/* ${dir_build}/templates/widget-listview/
.PHONY: templates-widgets-weekview .PHONY: templates-widgets-weekview
templates-widgets-weekview: \ templates-widgets-weekview: \
$(wildcard ${dir_source}/widgets/weekview/templates/*) $(wildcard ${dir_source}/widgets/weekview/templates/*)
@ ${cmd_log} "templates:widgets:weekview …" @ ${cmd_log} "templates:widget:weekview …"
@ ${cmd_mkdir} ${dir_build}/templates/widget-weekview @ ${cmd_mkdir} ${dir_build}/templates/widget-weekview
@ ${cmd_cp} -r -u -v ${dir_source}/widgets/weekview/templates/* ${dir_build}/templates/widget-weekview/ @ ${cmd_cp} -r -u -v ${dir_source}/widgets/weekview/templates/* ${dir_build}/templates/widget-weekview/
.PHONY: templates-widgets-mode_switcher .PHONY: templates-widgets-mode_switcher
templates-widgets-mode_switcher: \ templates-widgets-mode_switcher: \
$(wildcard ${dir_source}/widgets/mode_switcher/templates/*) $(wildcard ${dir_source}/widgets/mode_switcher/templates/*)
@ ${cmd_log} "templates:widgets:mode_switcher …" @ ${cmd_log} "templates:widget:mode_switcher …"
@ ${cmd_mkdir} ${dir_build}/templates/widget-mode_switcher @ ${cmd_mkdir} ${dir_build}/templates/widget-mode_switcher
@ ${cmd_cp} -r -u -v ${dir_source}/widgets/mode_switcher/templates/* ${dir_build}/templates/widget-mode_switcher/ @ ${cmd_cp} -r -u -v ${dir_source}/widgets/mode_switcher/templates/* ${dir_build}/templates/widget-mode_switcher/
.PHONY: templates-widgets-calendar_edit
templates-widgets-calendar_edit: \
$(wildcard ${dir_source}/widgets/calendar_edit/templates/*)
@ ${cmd_log} "templates:widgets:calendar_edit …"
@ ${cmd_mkdir} ${dir_build}/templates/widget-calendar_edit
# @ ${cmd_cp} -r -u -v ${dir_source}/widgets/calendar_edit/templates/* ${dir_build}/templates/widget-calendar_edit/
.PHONY: templates-widgets-event_edit
templates-widgets-event_edit: \
$(wildcard ${dir_source}/pages/event_edit/templates/*)
@ ${cmd_log} "templates:widgets:event_edit …"
@ ${cmd_mkdir} ${dir_build}/templates/widget-event_edit
# @ ${cmd_cp} -r -u -v ${dir_source}/pages/event_edit/templates/* ${dir_build}/templates/widget-uevent_edit/
.PHONY: templates-pages-caldav .PHONY: templates-pages-caldav
templates-pages-caldav: \ templates-pages-caldav: \
$(wildcard ${dir_source}/pages/caldav/templates/*) $(wildcard ${dir_source}/pages/caldav/templates/*)
@ ${cmd_log} "templates:caldav …" @ ${cmd_log} "templates:page:caldav …"
@ ${cmd_mkdir} ${dir_build}/templates/caldav @ ${cmd_mkdir} ${dir_build}/templates/caldav
@ ${cmd_cp} -r -u -v ${dir_source}/pages/caldav/templates/* ${dir_build}/templates/caldav/ @ ${cmd_cp} -r -u -v ${dir_source}/pages/caldav/templates/* ${dir_build}/templates/caldav/
.PHONY: templates-pages-overview .PHONY: templates-pages-overview
templates-pages-overview: \ templates-pages-overview: \
$(wildcard ${dir_source}/pages/overview/templates/*) $(wildcard ${dir_source}/pages/overview/templates/*)
@ ${cmd_log} "templates:overview …" @ ${cmd_log} "templates:page:overview …"
@ ${cmd_mkdir} ${dir_build}/templates/overview @ ${cmd_mkdir} ${dir_build}/templates/overview
@ ${cmd_cp} -r -u -v ${dir_source}/pages/overview/templates/* ${dir_build}/templates/overview/ @ ${cmd_cp} -r -u -v ${dir_source}/pages/overview/templates/* ${dir_build}/templates/overview/
.PHONY: style .PHONY: style
style: \ style: ${dir_build}/style.css
$(wildcard ${dir_source}/style/*)
${dir_build}/style.css: \
$(wildcard ${dir_source}/style/*.css) \
$(wildcard ${dir_source}/widgets/*/style.css) \
$(wildcard ${dir_source}/pages/*/style.css)
@ ${cmd_log} "style …" @ ${cmd_log} "style …"
@ ${cmd_mkdir} ${dir_build} @ ${cmd_mkdir} ${dir_build}
@ ${cmd_cat} ${dir_source}/style/* > ${dir_build}/style.css @ ${cmd_cat} $^ > $@
.PHONY: logic .PHONY: logic
logic: ${dir_build}/logic.js logic: ${dir_build}/logic.js
@ -123,18 +109,9 @@ ${dir_temp}/logic-unlinked.js: \
${dir_source}/types.ts \ ${dir_source}/types.ts \
${dir_source}/model.ts \ ${dir_source}/model.ts \
${dir_source}/helpers.ts \ ${dir_source}/helpers.ts \
${dir_source}/widgets/login/logic.ts \
${dir_source}/widgets/menu/logic.ts \
${dir_source}/widgets/sources/logic.ts \
${dir_source}/widgets/listview/logic.ts \
${dir_source}/widgets/weekview/logic.ts \
${dir_source}/widgets/mode_switcher/logic.ts \
${dir_source}/widgets/calendar_edit/logic.ts \
${dir_source}/widgets/event_edit/logic.ts \
${dir_source}/overlay.ts \ ${dir_source}/overlay.ts \
${dir_source}/pages/caldav/logic.ts \ $(wildcard ${dir_source}/widgets/*/logic.ts) \
${dir_source}/pages/oidc_finish/logic.ts \ $(wildcard ${dir_source}/pages/*/logic.ts) \
${dir_source}/pages/overview/logic.ts \
${dir_source}/main.ts ${dir_source}/main.ts
@ ${cmd_log} "logic | compile …" @ ${cmd_log} "logic | compile …"
@ ${cmd_mkdir} $(dir $@) @ ${cmd_mkdir} $(dir $@)