Compare commits

...

13 commits

Author SHA1 Message Date
fenris 207ea459ae [mod] overlay:scrollable 2025-10-30 08:32:33 +01:00
fenris 74e95dff54 [mod] focussing 2025-10-29 13:24:41 +01:00
fenris b0bd963145 [mod] Aufräum-Aktionen und kleinere Anpassungen 2025-10-29 11:40:27 +01:00
fenris 58fc2ea97f [res] 2025-10-28 11:44:26 +01:00
fenris 44c70a10bb [task-419]
Co-authored-by: Fenris Wolf <fenris@folksprak.org>
Co-committed-by: Fenris Wolf <fenris@folksprak.org>
2025-10-28 11:41:17 +01:00
fenris 63504f4e70 [mod] Tages-Beschriftung in Wochenansichts-Zellen 2025-10-26 21:46:58 +01:00
fenris fae611bec4 [mod] calendar default access level -> none 2025-10-24 18:55:10 +02:00
fenris b383460f3b [task-426] 2025-10-24 12:38:28 +02:00
fenris 9e24750d4d [mod] queueing ausgelagern 2025-10-24 00:23:53 +02:00
fenris c0abe0a180 [upd] plankton 2025-10-24 00:23:29 +02:00
fenris 2b11b57818 [task-409] 2025-10-23 23:16:11 +02:00
fenris 004b9f3211 [fix] logging:threshold 2025-10-23 21:58:51 +02:00
fenris ccb408b1b0 Merge pull request 'Gruppen-Steuerung' (#3) from task-416 into main
Reviewed-on: #3
2025-10-23 19:16:40 +02:00
66 changed files with 3342 additions and 1709 deletions

View file

@ -887,6 +887,35 @@ declare namespace lib_plankton.call {
export function null_prop<type_value_from, type_value_to>(value_from: (null | type_value_from), function_: ((value: type_value_from) => type_value_to)): (null | type_value_to); export function null_prop<type_value_from, type_value_to>(value_from: (null | type_value_from), function_: ((value: type_value_from) => type_value_to)): (null | type_value_to);
export {}; export {};
} }
declare namespace lib_plankton.call {
/**
*/
type type_queue_item<type_data, type_result> = {
data: type_data;
resolve: ((result: type_result) => void);
reject: ((reason: any) => void);
};
/**
*/
export type type_queue<type_data, type_result> = {
handler: ((data: type_data) => Promise<type_result>);
items: Array<type_queue_item<type_data, type_result>>;
busy: boolean;
paused: boolean;
};
/**
*/
export function queue_make<type_data, type_result>(handler: ((data: type_data) => Promise<type_result>)): type_queue<type_data, type_result>;
/**
*/
export function queue_toggle_paused<type_data, type_result>(queue_subject: type_queue<type_data, type_result>, { "mode": mode, }?: {
mode?: (null | boolean);
}): void;
/**
*/
export function queue_add<type_data, type_result>(queue_subject: type_queue<type_data, type_result>, data: type_data, resolve: ((result: type_result) => void), reject: ((reason: any) => void)): void;
export {};
}
declare namespace lib_plankton.file { declare namespace lib_plankton.file {
/** /**
* @author fenris * @author fenris
@ -4165,6 +4194,49 @@ declare namespace lib_plankton.zoo_widget {
load(target_element: HTMLElement): Promise<void>; load(target_element: HTMLElement): Promise<void>;
} }
} }
declare namespace lib_plankton.zoo_widget {
/**
*/
class class_slider implements interface_widget {
/**
*/
private conf;
/**
*/
private state;
/**
*/
constructor(content: Array<interface_widget>, { "threshold": threshold, "acceleration_function": acceleration_function, "initial_index": initial_index, }?: {
threshold?: float;
acceleration_function?: ((float: any) => float);
initial_index?: int;
});
/**
*/
private static cap;
/**
*/
private static position_subtract;
/**
*/
private static touch_event_position;
/**
*/
private set_translation;
/**
*/
private start;
/**
*/
private update;
/**
*/
private finish;
/**
*/
load(target_element: HTMLElement): Promise<void>;
}
}
declare namespace lib_plankton.zoo_page { declare namespace lib_plankton.zoo_page {
/** /**
*/ */
@ -4234,6 +4306,9 @@ declare namespace lib_plankton.zoo_input {
/** /**
*/ */
hook_change(action: (() => void)): Promise<void>; hook_change(action: (() => void)): Promise<void>;
/**
*/
focus(): void;
} }
} }
declare namespace lib_plankton.zoo_input { declare namespace lib_plankton.zoo_input {
@ -4281,6 +4356,10 @@ declare namespace lib_plankton.zoo_input {
* [implementation] * [implementation]
*/ */
hook_change(action: (() => void)): Promise<void>; hook_change(action: (() => void)): Promise<void>;
/**
* [implementation]
*/
focus(): void;
} }
} }
declare namespace lib_plankton.zoo_input { declare namespace lib_plankton.zoo_input {
@ -4320,6 +4399,10 @@ declare namespace lib_plankton.zoo_input {
* [implementation] * [implementation]
*/ */
hook_change(action: (() => void)): Promise<void>; hook_change(action: (() => void)): Promise<void>;
/**
* [implementation]
*/
focus(): void;
} }
} }
declare namespace lib_plankton.zoo_input { declare namespace lib_plankton.zoo_input {
@ -4367,6 +4450,10 @@ declare namespace lib_plankton.zoo_input {
* [implementation] * [implementation]
*/ */
hook_change(action: (() => void)): Promise<void>; hook_change(action: (() => void)): Promise<void>;
/**
* [implementation]
*/
focus(): void;
} }
} }
declare namespace lib_plankton.zoo_input { declare namespace lib_plankton.zoo_input {
@ -4405,6 +4492,10 @@ declare namespace lib_plankton.zoo_input {
* [implementation] * [implementation]
*/ */
hook_change(action: (() => void)): Promise<void>; hook_change(action: (() => void)): Promise<void>;
/**
* [implementation]
*/
focus(): void;
} }
} }
declare namespace lib_plankton.zoo_input { declare namespace lib_plankton.zoo_input {
@ -4461,6 +4552,10 @@ declare namespace lib_plankton.zoo_input {
* [implementation] * [implementation]
*/ */
hook_change(action: (() => void)): Promise<void>; hook_change(action: (() => void)): Promise<void>;
/**
* [implementation]
*/
focus(): void;
} }
} }
declare namespace lib_plankton.zoo_input { declare namespace lib_plankton.zoo_input {
@ -4499,6 +4594,10 @@ declare namespace lib_plankton.zoo_input {
* [implementation] * [implementation]
*/ */
hook_change(action: (() => void)): Promise<void>; hook_change(action: (() => void)): Promise<void>;
/**
* [implementation]
*/
focus(): void;
} }
} }
declare namespace lib_plankton.zoo_input { declare namespace lib_plankton.zoo_input {
@ -4545,6 +4644,10 @@ declare namespace lib_plankton.zoo_input {
* [implementation] * [implementation]
*/ */
hook_change(action: (() => void)): Promise<void>; hook_change(action: (() => void)): Promise<void>;
/**
* [implementation]
*/
focus(): void;
} }
} }
declare namespace lib_plankton.zoo_input { declare namespace lib_plankton.zoo_input {
@ -4584,6 +4687,10 @@ declare namespace lib_plankton.zoo_input {
* [implementation] * [implementation]
*/ */
hook_change(action: (() => void)): Promise<void>; hook_change(action: (() => void)): Promise<void>;
/**
* [implementation]
*/
focus(): void;
} }
} }
declare namespace lib_plankton.zoo_input { declare namespace lib_plankton.zoo_input {
@ -4630,6 +4737,10 @@ declare namespace lib_plankton.zoo_input {
* [implementation] * [implementation]
*/ */
hook_change(action: (() => void)): Promise<void>; hook_change(action: (() => void)): Promise<void>;
/**
* [implementation]
*/
focus(): void;
} }
} }
declare namespace lib_plankton.zoo_input { declare namespace lib_plankton.zoo_input {
@ -4693,6 +4804,10 @@ declare namespace lib_plankton.zoo_input {
* [implementation] * [implementation]
*/ */
hook_change(action: (() => void)): Promise<void>; hook_change(action: (() => void)): Promise<void>;
/**
* [implementation]
*/
focus(): void;
} }
export {}; export {};
} }
@ -4737,6 +4852,10 @@ declare namespace lib_plankton.zoo_input {
* [implementation] * [implementation]
*/ */
hook_change(action: (() => void)): Promise<void>; hook_change(action: (() => void)): Promise<void>;
/**
* [implementation]
*/
focus(): void;
} }
} }
declare namespace lib_plankton.zoo_input { declare namespace lib_plankton.zoo_input {
@ -4789,6 +4908,10 @@ declare namespace lib_plankton.zoo_input {
* [implementation] * [implementation]
*/ */
hook_change(action: (() => void)): Promise<void>; hook_change(action: (() => void)): Promise<void>;
/**
* [implementation]
*/
focus(): void;
} }
} }
declare namespace lib_plankton.zoo_input { declare namespace lib_plankton.zoo_input {
@ -4833,6 +4956,10 @@ declare namespace lib_plankton.zoo_input {
* [implementation] * [implementation]
*/ */
hook_change(action: (() => void)): Promise<void>; hook_change(action: (() => void)): Promise<void>;
/**
* [implementation]
*/
focus(): void;
} }
} }
declare namespace lib_plankton.zoo_input { declare namespace lib_plankton.zoo_input {
@ -4881,6 +5008,10 @@ declare namespace lib_plankton.zoo_input {
* [implementation] * [implementation]
*/ */
hook_change(action: (() => void)): Promise<void>; hook_change(action: (() => void)): Promise<void>;
/**
* [implementation]
*/
focus(): void;
} }
} }
declare namespace lib_plankton.zoo_input { declare namespace lib_plankton.zoo_input {
@ -4916,6 +5047,10 @@ declare namespace lib_plankton.zoo_input {
* [implementation] * [implementation]
*/ */
hook_change(action: (() => void)): Promise<void>; hook_change(action: (() => void)): Promise<void>;
/**
* [implementation]
*/
focus(): void;
} }
} }
declare namespace lib_plankton.zoo_input { declare namespace lib_plankton.zoo_input {
@ -4949,6 +5084,10 @@ declare namespace lib_plankton.zoo_input {
* [implementation] * [implementation]
*/ */
hook_change(action: (() => void)): Promise<void>; hook_change(action: (() => void)): Promise<void>;
/**
* [implementation]
*/
focus(): void;
} }
} }
declare namespace lib_plankton.zoo_input { declare namespace lib_plankton.zoo_input {
@ -4982,6 +5121,10 @@ declare namespace lib_plankton.zoo_input {
* [implementation] * [implementation]
*/ */
hook_change(action: (() => void)): Promise<void>; hook_change(action: (() => void)): Promise<void>;
/**
* [implementation]
*/
focus(): void;
} }
} }
declare namespace lib_plankton.zoo_input { declare namespace lib_plankton.zoo_input {
@ -5094,6 +5237,9 @@ declare namespace lib_plankton.zoo_form {
/** /**
*/ */
input_lock(mode: boolean): Promise<void>; input_lock(mode: boolean): Promise<void>;
/**
*/
input_focus(): void;
} }
export {}; export {};
} }

File diff suppressed because it is too large Load diff

View file

@ -7,7 +7,7 @@
"path": "" "path": ""
}, },
"misc": { "misc": {
"oidc_redirect_uri_template": "http://localhost:8888/#oidc_finish,session_key={{session_key}}", "oidc_redirect_uri_template": "http://localhost:8888/?action=oidc_finish&session_key={{session_key}}",
"use_central_europe_specific_datetime_inputs": true "use_central_europe_specific_datetime_inputs": true
} }
} }

View file

@ -1,3 +1,23 @@
/*
This file is part of »dali«.
Copyright 2025 'kcf' <fenris@folksprak.org>
»dali« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»dali« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »dali«. If not, see <http://www.gnu.org/licenses/>.
*/
namespace _dali namespace _dali
{ {
@ -86,9 +106,20 @@ namespace _dali
{ {
await _dali.backend.set_session_key(session_key); await _dali.backend.set_session_key(session_key);
const status = await _dali.backend.status(); const status = await _dali.backend.status();
if (! status.logged_in)
{
lib_plankton.log.error(
"dali.oidc_login_failed"
);
await _dali.notify_logout();
return Promise.reject<void>(new Error("oidc login failed"));
}
else
{
await _dali.notify_login(status.name); await _dali.notify_login(status.name);
return Promise.resolve<void>(undefined); return Promise.resolve<void>(undefined);
} }
}
/** /**

View file

@ -14,7 +14,20 @@
"common.weekday.friday": "Fr", "common.weekday.friday": "Fr",
"common.weekday.saturday": "Sa", "common.weekday.saturday": "Sa",
"common.weekday.sunday": "So", "common.weekday.sunday": "So",
"common.monthname.january": "Jan",
"common.monthname.february": "Feb",
"common.monthname.march": "Mär",
"common.monthname.april": "Apr",
"common.monthname.may": "Mai",
"common.monthname.june": "Jun",
"common.monthname.july": "Jul",
"common.monthname.august": "Aug",
"common.monthname.september": "Sep",
"common.monthname.october": "Okt",
"common.monthname.november": "Nov",
"common.monthname.december": "Dez",
"common.open": "öffnen", "common.open": "öffnen",
"common.close": "schließen",
"common.edit": "bearbeiten", "common.edit": "bearbeiten",
"common.show": "zeigen", "common.show": "zeigen",
"common.hide": "ausblenden", "common.hide": "ausblenden",
@ -66,27 +79,20 @@
"widget.login.internal.password": "Kennwort", "widget.login.internal.password": "Kennwort",
"widget.login.internal.do": "Anmelden", "widget.login.internal.do": "Anmelden",
"widget.login.oidc.via": "via {{title}}", "widget.login.oidc.via": "via {{title}}",
"page.caldav.title": "CalDAV", "widget.caldav.title": "CalDAV",
"page.caldav.unavailable": "CalDAV nicht verfügbar", "widget.caldav.unavailable": "CalDAV nicht verfügbar",
"page.caldav.conf.title": "Zugangsdaten", "widget.caldav.conf.title": "Zugangsdaten",
"page.caldav.conf.address": "Adresse (URL)", "widget.caldav.conf.address": "Adresse (URL)",
"page.caldav.conf.username": "Nutzername", "widget.caldav.conf.username": "Nutzername",
"page.caldav.conf.password": "Kennwort", "widget.caldav.conf.password": "Kennwort",
"page.caldav.conf.setup_hints": "Einrichtungs-Hinweise", "widget.caldav.conf.setup_hints": "Einrichtungs-Hinweise",
"page.caldav.conf.token_unset": "es muss zunächst ein Token gesetzt werden", "widget.caldav.conf.token_unset": "es muss zunächst ein Token gesetzt werden",
"page.caldav.set_token.title": "Token setzen", "widget.caldav.set_token.title": "Token setzen",
"page.caldav.set_token.action.set": "setzen", "widget.caldav.set_token.action.set": "setzen",
"page.caldav.set_token.action.overwrite": "überschreiben", "widget.caldav.set_token.action.overwrite": "überschreiben",
"page.calendar_add.title": "Kalendar anlegen", "widget.overview.title": "Übersicht",
"page.calendar_add.actions.do": "anlegen", "widget.overview.login_hint": "anmelden um nicht-öffentliche Termine zu sehen",
"page.calendar_edit.title.regular": "Kalendar bearbeiten", "widget.overview.mode.week": "Wochen-Ansicht",
"page.calendar_edit.title.read_only": "Kalendar-Details", "widget.overview.mode.list": "Listen-Ansicht"
"page.event_add.title": "Termin anlegen",
"page.event_edit.title.regular": "Termin bearbeiten",
"page.event_edit.title.read_only": "Termin-Details",
"page.overview.title": "Übersicht",
"page.overview.login_hint": "anmelden um nicht-öffentliche Termine zu sehen",
"page.overview.mode.week": "Wochen-Ansicht",
"page.overview.mode.list": "Listen-Ansicht"
} }
} }

View file

@ -14,7 +14,20 @@
"common.weekday.friday": "Fri", "common.weekday.friday": "Fri",
"common.weekday.saturday": "Sat", "common.weekday.saturday": "Sat",
"common.weekday.sunday": "Sun", "common.weekday.sunday": "Sun",
"common.monthname.january": "jan",
"common.monthname.february": "feb",
"common.monthname.march": "mar",
"common.monthname.april": "apr",
"common.monthname.may": "may",
"common.monthname.june": "jun",
"common.monthname.july": "jul",
"common.monthname.august": "aug",
"common.monthname.september": "sep",
"common.monthname.october": "oct",
"common.monthname.november": "nov",
"common.monthname.december": "dec",
"common.open": "open", "common.open": "open",
"common.close": "close",
"common.edit": "edit", "common.edit": "edit",
"common.show": "show", "common.show": "show",
"common.hide": "hide", "common.hide": "hide",
@ -66,27 +79,20 @@
"widget.login.internal.password": "password", "widget.login.internal.password": "password",
"widget.login.internal.do": "login", "widget.login.internal.do": "login",
"widget.login.oidc.via": "via {{title}}", "widget.login.oidc.via": "via {{title}}",
"page.caldav.title": "CalDAV", "widget.caldav.title": "CalDAV",
"page.caldav.unavailable": "CalDAV not available", "widget.caldav.unavailable": "CalDAV not available",
"page.caldav.conf.title": "credentials", "widget.caldav.conf.title": "credentials",
"page.caldav.conf.address": "address (URL)", "widget.caldav.conf.address": "address (URL)",
"page.caldav.conf.username": "username", "widget.caldav.conf.username": "username",
"page.caldav.conf.password": "password", "widget.caldav.conf.password": "password",
"page.caldav.conf.setup_hints": "setup hints", "widget.caldav.conf.setup_hints": "setup hints",
"page.caldav.conf.token_unset": "a token has to be set", "widget.caldav.conf.token_unset": "a token has to be set",
"page.caldav.set_token.title": "set token", "widget.caldav.set_token.title": "set token",
"page.caldav.set_token.action.set": "set", "widget.caldav.set_token.action.set": "set",
"page.caldav.set_token.action.overwrite": "overwrite", "widget.caldav.set_token.action.overwrite": "overwrite",
"page.calendar_add.title": "Add calendar", "widget.overview.title": "Overview",
"page.calendar_add.actions.do": "anlegen", "widget.overview.login_hint": "log in to view non-public events",
"page.event_add.title": "Add event", "widget.overview.mode.week": "week view",
"page.calendar_edit.title.regular": "Edit calendar", "widget.overview.mode.list": "list view"
"page.calendar_edit.title.read_only": "Calendar details",
"page.event_edit.title.regular": "Edit event",
"page.event_edit.title.read_only": "Event details",
"page.overview.title": "Overview",
"page.overview.login_hint": "log in to view non-public events",
"page.overview.mode.week": "week view",
"page.overview.mode.list": "list view"
} }
} }

View file

@ -1,9 +1,45 @@
/*
This file is part of »dali«.
Copyright 2025 'kcf' <fenris@folksprak.org>
»dali« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»dali« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »dali«. If not, see <http://www.gnu.org/licenses/>.
*/
/** /**
*/ */
namespace _dali.helpers namespace _dali.helpers
{ {
/**
* @todo outsource
*/
function is_touch_device(
)
: boolean
{
return (
("ontouchstart" in window)
||
(navigator.maxTouchPoints > 0)
||
(navigator["msMaxTouchPoints"] > 0)
);
}
/** /**
*/ */
var _template_cache : Record<string, string> = {}; var _template_cache : Record<string, string> = {};
@ -13,12 +49,13 @@ namespace _dali.helpers
*/ */
export function view_mode_determine( export function view_mode_determine(
mode_descriptor : string mode_descriptor : string
) : _dali.enum_view_mode )
: _dali.enum_view_mode
{ {
if (mode_descriptor === "auto") if (mode_descriptor === "auto")
{ {
return ( return (
(window.innerWidth >= 800) (window.innerWidth >= 1000)
? ?
_dali.enum_view_mode.week _dali.enum_view_mode.week
: :
@ -32,6 +69,30 @@ namespace _dali.helpers
} }
/**
*/
export function view_kind_determine(
mode_descriptor : string
)
: _dali.enum_view_kind
{
if (mode_descriptor === "auto")
{
return (
is_touch_device()
?
_dali.enum_view_kind.touch
:
_dali.enum_view_kind.regular
);
}
else
{
return view_kind_decode(mode_descriptor);
}
}
/** /**
*/ */
export async function template_coin( export async function template_coin(
@ -77,6 +138,25 @@ namespace _dali.helpers
} }
/**
*/
export async function element_from_template(
group : string,
name : string,
data : Record<string, string>
)
: Promise<HTMLElement>
{
const dom_dummy = document.createElement("div");
dom_dummy.innerHTML = await _dali.helpers.template_coin(
group,
name,
data
);
return (dom_dummy.children[0] as HTMLElement);
}
/** /**
* @todo outsource * @todo outsource
*/ */
@ -84,7 +164,8 @@ namespace _dali.helpers
members : Array< members : Array<
() => Promise<type_result> () => Promise<type_result>
> >
) : Promise< )
: Promise<
Array< Array<
type_result type_result
> >
@ -101,7 +182,8 @@ namespace _dali.helpers
/** /**
*/ */
export function input_access_level( export function input_access_level(
) : lib_plankton.zoo_input.interface_input<_dali.enum_access_level> )
: lib_plankton.zoo_input.interface_input<_dali.enum_access_level>
{ {
return ( return (
new lib_plankton.zoo_input.class_input_wrapped< new lib_plankton.zoo_input.class_input_wrapped<
@ -222,7 +304,8 @@ namespace _dali.helpers
/** /**
*/ */
export function datetime_input( export function datetime_input(
) : lib_plankton.zoo_input.interface_input<lib_plankton.pit.type_datetime> )
: lib_plankton.zoo_input.interface_input<lib_plankton.pit.type_datetime>
{ {
return ( return (
_dali.conf.get().misc.use_central_europe_specific_datetime_inputs _dali.conf.get().misc.use_central_europe_specific_datetime_inputs
@ -264,4 +347,48 @@ namespace _dali.helpers
); );
} }
/**
*/
export function month_name(
month : int
)
: string
{
const keys : Array<string> = [
"common.monthname.january",
"common.monthname.february",
"common.monthname.march",
"common.monthname.april",
"common.monthname.may",
"common.monthname.june",
"common.monthname.july",
"common.monthname.august",
"common.monthname.september",
"common.monthname.october",
"common.monthname.november",
"common.monthname.december",
];
return lib_plankton.translate.get(keys[month-1]);
}
/**
*/
export function loading(
mode : boolean
)
: void
{
if (! mode)
{
_dali.overlay.toggle({"mode": false});
}
else
{
_dali.overlay.get_content_element().innerHTML = ". . .";
_dali.overlay.toggle({"mode": true});
}
}
} }

View file

@ -1,42 +1,28 @@
/*
This file is part of »dali«.
Copyright 2025 'kcf' <fenris@folksprak.org>
»dali« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»dali« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »dali«. If not, see <http://www.gnu.org/licenses/>.
*/
/** /**
*/ */
namespace _dali namespace _dali
{ {
/**
*/
function nav_groups(
logged_in : boolean
)
: Array<string>
{
return (
logged_in
?
["logged_in"]
:
["logged_out"]
);
}
/**
* @todo reload page when switching to "logged_out"
*/
async function update(
)
: Promise<void>
{
lib_plankton.log.debug(
"dali.update"
);
const logged_in : boolean = _dali.is_logged_in();
lib_plankton.zoo_page.nav_set_groups(nav_groups(logged_in));
// lib_plankton.zoo_page.reload();
}
/** /**
*/ */
export async function main( export async function main(
@ -48,12 +34,27 @@ namespace _dali
"conf.json" "conf.json"
); );
// init // init:logger
lib_plankton.log.set_main_logger( lib_plankton.log.set_main_logger(
[ [
{"kind": "console", "data": {"threshold": "info"}}, {
"kind": "minlevel",
"data": {
"core": {
"kind": "console",
"data": {
}
},
"threshold": "info"
}
},
] ]
); );
// init:overlay
{
await _dali.overlay.initialize();
_dali.helpers.loading(true);
}
// init:localization // init:localization
{ {
const order : Array<string> = ["deu", "eng"]; const order : Array<string> = ["deu", "eng"];
@ -62,10 +63,11 @@ namespace _dali
"verbosity": 1, "verbosity": 1,
"packages": await Promise.all( "packages": await Promise.all(
order.map( order.map(
async (code) => JSON.parse( code => (
await lib_plankton.file.read( Promise.resolve(code)
"data/localization/" + code + ".loc.json" .then<string>(code => Promise.resolve(lib_plankton.string.coin("data/localization/{{code}}.loc.json", {"code": code})))
) .then<string>(lib_plankton.file.read)
.then<any>(content => Promise.resolve(JSON.parse(content)))
) )
) )
), ),
@ -74,11 +76,14 @@ namespace _dali
} }
); );
} }
// init:backend
await _dali.backend.initialize( await _dali.backend.initialize(
_dali.conf.get()["backend"] _dali.conf.get()["backend"]
); );
// init:model
await _dali.model.initialize( await _dali.model.initialize(
); );
// init:page
lib_plankton.zoo_page.init( lib_plankton.zoo_page.init(
document.querySelector("main"), document.querySelector("main"),
{ {
@ -86,69 +91,55 @@ namespace _dali
"name": "overview", "name": "overview",
"parameters": {} "parameters": {}
}, },
/*
"nav_entries": [
],
*/
"nav_initial_groups": [],
} }
); );
// menu widget // init:menu
{ {
const widget_menu : _dali.widgets.menu.class_widget_menu = new _dali.widgets.menu.class_widget_menu( const widget_menu : _dali.widgets.class_widget_menu = new _dali.widgets.class_widget_menu(
[ [
{ {
"label": lib_plankton.translate.get("common.login"), "label": lib_plankton.translate.get("common.login"),
"groups": ["logged_out"], "groups": ["logged_out"],
"action": () => { "action": async () => {
const widget_login = new _dali.widgets.login.class_widget_login( _dali.helpers.loading(true);
const widget : lib_plankton.zoo_widget.interface_widget = new _dali.widgets.class_widget_login(
{ {
"action_cancel": () => { "action_cancel": () => {
_dali.overlay.clear(); _dali.overlay.clear();
_dali.overlay.toggle({"mode": false}); _dali.overlay.toggle({"mode": false});
}, },
"action_success": async () => { "action_success": async () => {
_dali.helpers.loading(true);
const status = await _dali.backend.status(); const status = await _dali.backend.status();
_dali.notify_login(status.name); _dali.notify_login(status.name);
_dali.overlay.clear(); // _dali.overlay.clear();
_dali.overlay.toggle({"mode": false}); _dali.helpers.loading(false);
}, },
} }
); );
_dali.overlay.clear(); await widget.load(_dali.overlay.get_content_element());
// _dali.helpers.loading(false);
_dali.overlay.toggle({"mode": true}); _dali.overlay.toggle({"mode": true});
widget_login.load(_dali.overlay.get_content_element());
}, },
}, },
{ {
"label": lib_plankton.translate.get("page.overview.title"), "label": lib_plankton.translate.get("widget.caldav.title"),
"groups": ["logged_out", "logged_in"],
"action": () => {
lib_plankton.zoo_page.set(
{
"name": "overview",
"parameters": {}
}
);
},
},
{
"label": lib_plankton.translate.get("page.caldav.title"),
"groups": ["logged_in"], "groups": ["logged_in"],
"action": () => { "action": async () => {
lib_plankton.zoo_page.set( _dali.helpers.loading(true);
{ const widget : lib_plankton.zoo_widget.interface_widget = new _dali.widgets.class_widget_caldav();
"name": "caldav", await widget.load(_dali.overlay.get_content_element());
"parameters": {} // _dali.helpers.loading(false);
} _dali.overlay.toggle({"mode": true});
);
}, },
}, },
{ {
"label": lib_plankton.translate.get("common.logout"), "label": lib_plankton.translate.get("common.logout"),
"groups": ["logged_in"], "groups": ["logged_in"],
"action": () => { "action": async () => {
_dali.logout(); _dali.helpers.loading(true);
await _dali.logout();
_dali.helpers.loading(false);
}, },
}, },
] ]
@ -167,18 +158,36 @@ namespace _dali
} }
); );
} }
await update();
await _dali.overlay.initialize();
/*
lib_plankton.call.loop(
() => {
update();
},
_dali.conf.get().misc.update_interval
);
*/
// check if logged_in // process actions
{
let url_search_params : URLSearchParams = new URLSearchParams(window.location.search);
const action : (null | string) = url_search_params.get("action");
switch (action)
{
case "oidc_finish":
{
try
{
await _dali.oidc_finish(url_search_params.get("session_key"));
}
catch (error_)
{
// do nothing
}
document.location = "/";
break;
}
default:
case null:
{
// do nothing
break;
}
}
}
// process status
{ {
const status = await _dali.backend.status(); const status = await _dali.backend.status();
lib_plankton.log.info( lib_plankton.log.info(
@ -195,7 +204,15 @@ namespace _dali
} }
} }
lib_plankton.zoo_page.start(); // load overview
{
const widget : lib_plankton.zoo_widget.interface_widget = (
new _dali.widgets.class_widget_overview(
)
);
await widget.load(document.querySelector("main"));
}
_dali.helpers.loading(false);
return Promise.resolve<void>(undefined); return Promise.resolve<void>(undefined);
} }

View file

@ -1,3 +1,23 @@
/*
This file is part of »dali«.
Copyright 2025 'kcf' <fenris@folksprak.org>
»dali« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»dali« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »dali«. If not, see <http://www.gnu.org/licenses/>.
*/
namespace _dali.model namespace _dali.model
{ {

View file

@ -1,10 +1,31 @@
/*
This file is part of »dali«.
Copyright 2025 'kcf' <fenris@folksprak.org>
»dali« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»dali« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »dali«. If not, see <http://www.gnu.org/licenses/>.
*/
namespace _dali.overlay namespace _dali.overlay
{ {
/** /**
*/ */
function get_container_element( function get_container_element(
) : HTMLElement )
: HTMLElement
{ {
return document.querySelector("#overlay"); return document.querySelector("#overlay");
} }
@ -13,7 +34,8 @@ namespace _dali.overlay
/** /**
*/ */
export function get_content_element( export function get_content_element(
) : HTMLElement )
: HTMLElement
{ {
return document.querySelector("#overlay_content"); return document.querySelector("#overlay_content");
} }
@ -22,7 +44,8 @@ namespace _dali.overlay
/** /**
*/ */
export function clear( export function clear(
) : void )
: void
{ {
get_content_element().innerHTML = ""; get_content_element().innerHTML = "";
} }
@ -41,7 +64,8 @@ namespace _dali.overlay
= =
{ {
} }
) : void )
: void
{ {
get_container_element().classList.toggle("overlay_active", mode ?? undefined); get_container_element().classList.toggle("overlay_active", mode ?? undefined);
} }
@ -50,7 +74,8 @@ namespace _dali.overlay
/** /**
*/ */
export function initialize( export function initialize(
) : Promise<void> )
: Promise<void>
{ {
clear(); clear();
const container_element : HTMLElement = get_container_element(); const container_element : HTMLElement = get_container_element();

View file

@ -1,119 +0,0 @@
namespace _dali.pages
{
/**
*/
lib_plankton.zoo_page.register(
"caldav",
async (parameters, target_element) => {
target_element.innerHTML = "";
const conf = await _dali.backend.user_dav_conf();
target_element.innerHTML = await _dali.helpers.template_coin(
"caldav",
"main",
{
"label": lib_plankton.translate.get("page.caldav.title"),
"content": (
(conf === null)
?
await _dali.helpers.template_coin(
"caldav",
"unavailable",
{
"text": lib_plankton.translate.get("page.caldav.unavailable"),
}
)
:
await _dali.helpers.template_coin(
"caldav",
"available",
{
"conf_title": lib_plankton.translate.get("page.caldav.conf.title"),
"conf_content": (
(conf.password === null)
?
await _dali.helpers.template_coin(
"caldav",
"conf-token_unset",
{
"text": lib_plankton.translate.get("page.caldav.conf.token_unset")
}
)
:
await _dali.helpers.template_coin(
"caldav",
"conf-token_set",
{
"address_label": lib_plankton.translate.get("page.caldav.conf.address"),
"address_value": conf.address,
"username_label": lib_plankton.translate.get("page.caldav.conf.username"),
"username_value": conf.username,
"password_label": lib_plankton.translate.get("page.caldav.conf.password"),
"password_value": conf.password,
"setup_hints_label": lib_plankton.translate.get("page.caldav.conf.setup_hints"),
"setup_hint_entries": (
await lib_plankton.call.promise_condense<string, unknown>(
conf.setup_hints
.map(
entry => () => _dali.helpers.template_coin(
"caldav",
"conf-setup_hint_entry",
{
"text": entry.label,
"href": entry.link,
"remark": (
(entry.remark === null)
?
""
:
lib_plankton.string.coin(
" — {{content}}",
{
"content": entry.remark,
}
)
)
}
)
)
)
).join("")
}
)
),
"set_token_title": lib_plankton.translate.get("page.caldav.set_token.title"),
"set_token_action": (
(conf.password === null)
?
lib_plankton.translate.get("page.caldav.set_token.action.set")
:
lib_plankton.translate.get("page.caldav.set_token.action.overwrite")
),
}
)
)
}
);
/**
* logic: set token
*/
{
if (conf !== null)
{
document.querySelector("#caldav-set_token > button").addEventListener(
"click",
async () => {
await _dali.backend.user_dav_token();
lib_plankton.zoo_page.reload();
}
);
}
}
return Promise.resolve<void>(undefined);
}
);
}

View file

@ -1,18 +0,0 @@
<div class="caldav-conf-section" id="caldav-conf-address">
<span class="caldav-conf-section-label">{{address_label}}</span>
<span class="caldav-conf-section-value caldav-conf-section-value-regular">{{address_value}}</span>
</div>
<div class="caldav-conf-section" id="caldav-conf-username">
<span class="caldav-conf-section-label">{{username_label}}</span>
<span class="caldav-conf-section-value caldav-conf-section-value-regular">{{username_value}}</span>
</div>
<div class="caldav-conf-section" id="caldav-conf-password">
<span class="caldav-conf-section-label">{{password_label}}</span>
<span class="caldav-conf-section-value caldav-conf-section-value-regular">{{password_value}}</span>
</div>
<div class="caldav-conf-section" id="caldav-conf-setup_hints">
<span class="caldav-conf-section-label">{{setup_hints_label}}</span>
<ul>
{{setup_hint_entries}}
</ul>
</div>

View file

@ -1,4 +0,0 @@
<div id="caldav-conf-info">
({{text}})
</div>

View file

@ -1,6 +0,0 @@
<div id="caldav">
<h2>{{label}}</h2>
<div id="caldav-content">
{{content}}
</div>
</div>

View file

@ -1,3 +0,0 @@
<div id="caldav-info">
{{text}}
</div>

View file

@ -1,21 +0,0 @@
namespace _dali.pages
{
/**
*/
lib_plankton.zoo_page.register(
"oidc_finish",
async (parameters, target_element) => {
target_element.innerHTML = "";
await _dali.oidc_finish(parameters["session_key"]);
lib_plankton.zoo_page.set(
{
"name": "overview",
"parameters": {}
}
);
return Promise.resolve<void>(undefined);
}
);
}

View file

@ -1,46 +0,0 @@
#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,18 +0,0 @@
<div id="overview">
<div id="overview-head">
<div id="overview-hint">
</div>
<div id="overview-mode">
</div>
</div>
<div id="overview-body">
<div id="overview-pane-left">
</div>
<div id="overview-pane-right">
<div id="overview-pane-right-weekview">
</div>
<div id="overview-pane-right-listview">
</div>
</div>
</div>
</div>

View file

@ -1,3 +1,23 @@
/*
This file is part of »dali«.
Copyright 2025 'kcf' <fenris@folksprak.org>
»dali« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»dali« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »dali«. If not, see <http://www.gnu.org/licenses/>.
*/
/** /**
*/ */
namespace _dali.backend namespace _dali.backend
@ -107,16 +127,7 @@ namespace _dali.backend
/** /**
*/ */
var _queue : { var _queue : lib_plankton.call.type_queue<type_request, any>;
items : Array<
{
request : type_request;
resolve : ((result : any) => void);
reject : ((reason : any) => void);
}
>;
busy : boolean;
};
/** /**
@ -140,10 +151,9 @@ namespace _dali.backend
) )
*/ */
); );
_queue = { _queue = lib_plankton.call.queue_make<type_request, any>(
"items": [], call_real
"busy": false, );
};
return Promise.resolve<void>(undefined); return Promise.resolve<void>(undefined);
} }
@ -243,60 +253,18 @@ namespace _dali.backend
&& &&
(http_response.status_code < 300) (http_response.status_code < 300)
) )
) { )
{
return Promise.reject<any>(http_response.body.toString()); return Promise.reject<any>(http_response.body.toString());
} }
else { else
{
const output : any = lib_plankton.json.decode(http_response.body.toString()); const output : any = lib_plankton.json.decode(http_response.body.toString());
return Promise.resolve<any>(output); return Promise.resolve<any>(output);
} }
} }
/**
*/
async function process(
)
: Promise<void>
{
if (_queue.busy)
{
// do nothing
}
else
{
_queue.busy = true;
while (_queue.items.length > 0)
{
const entry = _queue.items.shift();
let successful : boolean;
let reason : any;
let result : any;
try
{
result = await call_real(entry.request);
successful = true;
}
catch (error)
{
reason = error;
successful = false;
}
if (successful)
{
entry.resolve(result);
}
else
{
entry.reject(reason);
}
}
_queue.busy = false;
// process();
}
}
/** /**
*/ */
async function call( async function call(
@ -311,19 +279,18 @@ namespace _dali.backend
"action": action, "action": action,
"input": input, "input": input,
}; };
const promise : Promise<any> = new Promise<any>( return (
new Promise<any>(
(resolve, reject) => { (resolve, reject) => {
_queue.items.push( lib_plankton.call.queue_add<type_request, any>(
{ _queue,
"request": request, request,
"resolve": resolve, resolve,
"reject": reject, reject
}
); );
} }
)
); );
process();
return promise;
} }

View file

@ -1,3 +1,23 @@
/*
This file is part of »dali«.
Copyright 2025 'kcf' <fenris@folksprak.org>
»dali« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»dali« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »dali«. If not, see <http://www.gnu.org/licenses/>.
*/
namespace _dali.conf namespace _dali.conf
{ {
@ -53,14 +73,15 @@ namespace _dali.conf
"default": "http://localhost:8888/#oidc_finish,session_key={{session_key}}" "default": "http://localhost:8888/#oidc_finish,session_key={{session_key}}"
}, },
"use_central_europe_specific_datetime_inputs": { "use_central_europe_specific_datetime_inputs": {
"nullable": true, "nullable": false,
"type": "boolean", "type": "boolean",
"default": false "default": false
}, },
"update_interval": { "weekview_cell_day_format": {
"nullable": false, "nullable": false,
"type": "integer", "type": "string",
"default": 60 "default": "d.b",
"description": "available placeholders: Y,m,b,d,W,w (as in UNIX command 'date')"
}, },
}, },
"required": [ "required": [
@ -78,7 +99,25 @@ namespace _dali.conf
/** /**
*/ */
var _data : (null | any) = null; type type_data = {
version : string;
backend : {
scheme : string;
host : string;
port : int;
path : string;
};
misc : {
oidc_redirect_uri_template : string;
use_central_europe_specific_datetime_inputs : string;
weekview_cell_day_format : string;
};
};
/**
*/
var _data : (null | type_data) = null;
/** /**
@ -93,7 +132,7 @@ namespace _dali.conf
/** /**
*/ */
export function get( export function get(
) : any ) : type_data
{ {
if (_data === null) if (_data === null)
{ {
@ -111,10 +150,7 @@ namespace _dali.conf
path : string path : string
) : Promise<void> ) : Promise<void>
{ {
_data = await lib_plankton.conf.load( _data = ((await lib_plankton.conf.load(_schema, path)) as type_data);
_schema,
path
);
return Promise.resolve<void>(undefined); return Promise.resolve<void>(undefined);
} }

View file

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

View file

@ -20,72 +20,6 @@ header
margin-bottom: 16px; margin-bottom: 16px;
} }
#overlay
{
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: hsla(var(--hue), 0%, 0%, 0.75);
z-index: 2;
}
#overlay_content
{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
-ms-transform: translate(-50%,-50%);
padding: 32px;
background-color: hsl(0, 0%, 12.5%);
color: hsl(0, 0%, 100%);
}
#overlay:not(.overlay_active)
{
display: none;
}
nav > ul
{
list-style-type: none;
margin: 0;
padding: 0;
}
nav > ul > li
{
display: inline-block;
margin: 8px;
padding: 8px;
}
nav > ul > li:not(.active)
{
display: none;
}
nav a
{
padding: 8px;
text-decoration: none;
color: hsl(var(--hue), 0%, 87.5%);
}
nav a:hover
{
color: hsl(var(--hue), 0%, 100%);
border-bottom: 2px solid hsl(0, 0%, 100%);
transition: 1s ease color;
}
a a
{ {
text-decoration: none; text-decoration: none;
@ -101,7 +35,7 @@ a:hover
button button
{ {
padding: 8px; padding: 8px 12px;
text-transform: uppercase; text-transform: uppercase;
cursor: pointer; cursor: pointer;

33
source/style/overlay.css Normal file
View file

@ -0,0 +1,33 @@
#overlay
{
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: hsla(var(--hue), 0%, 0%, 0.75);
z-index: 2;
overflow: auto;
}
#overlay_content
{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
-ms-transform: translate(-50%,-50%);
padding: 32px;
background-color: hsl(0, 0%, 12.5%);
color: hsl(0, 0%, 100%);
}
#overlay:not(.overlay_active)
{
display: none;
}

View file

@ -1,3 +1,22 @@
/*
This file is part of »dali«.
Copyright 2025 'kcf' <fenris@folksprak.org>
»dali« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»dali« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »dali«. If not, see <http://www.gnu.org/licenses/>.
*/
/** /**
*/ */
@ -6,7 +25,8 @@ namespace _dali
/** /**
*/ */
export enum enum_access_level { export enum enum_access_level
{
none, none,
view, view,
edit, edit,
@ -183,20 +203,12 @@ namespace _dali
}; };
/**
*/
export enum enum_view_mode
{
week,
list,
}
/** /**
*/ */
export function access_level_encode( export function access_level_encode(
access_level : _dali.enum_access_level access_level : _dali.enum_access_level
) : ("none" | "view" | "edit" | "admin") )
: ("none" | "view" | "edit" | "admin")
{ {
switch (access_level) switch (access_level)
{ {
@ -212,7 +224,8 @@ namespace _dali
*/ */
export function access_level_decode( export function access_level_decode(
representation : /*("none" | "view" | "edit" | "admin")*/string representation : /*("none" | "view" | "edit" | "admin")*/string
) : _dali.enum_access_level )
: _dali.enum_access_level
{ {
switch (representation) switch (representation)
{ {
@ -225,11 +238,21 @@ namespace _dali
} }
/**
*/
export enum enum_view_mode
{
week,
list,
}
/** /**
*/ */
export function view_mode_encode( export function view_mode_encode(
mode : _dali.enum_view_mode mode : _dali.enum_view_mode
) : string )
: string
{ {
switch (mode) switch (mode)
{ {
@ -244,7 +267,8 @@ namespace _dali
*/ */
export function view_mode_decode( export function view_mode_decode(
view_mode_encoded : string view_mode_encoded : string
) : _dali.enum_view_mode )
: _dali.enum_view_mode
{ {
const map : Record<string, _dali.enum_view_mode> = { const map : Record<string, _dali.enum_view_mode> = {
"week": _dali.enum_view_mode.week, "week": _dali.enum_view_mode.week,
@ -260,4 +284,51 @@ namespace _dali
} }
} }
/**
*/
export enum enum_view_kind
{
regular,
touch,
}
/**
*/
export function view_kind_encode(
kind : _dali.enum_view_kind
)
: string
{
switch (kind)
{
case _dali.enum_view_kind.regular: {return "regular"; break;}
case _dali.enum_view_kind.touch: {return "touch"; break;}
default: {throw (new Error("invalid kind"));}
}
}
/**
*/
export function view_kind_decode(
view_kind_encoded : string
)
: _dali.enum_view_kind
{
const map : Record<string, _dali.enum_view_kind> = {
"regular": _dali.enum_view_kind.regular,
"touch": _dali.enum_view_kind.touch,
};
if (! (view_kind_encoded in map))
{
throw (new Error("invalid kind: " + view_kind_encoded));
}
else
{
return map[view_kind_encoded];
}
}
} }

View file

@ -0,0 +1,173 @@
/*
This file is part of »dali«.
Copyright 2025 'kcf' <fenris@folksprak.org>
»dali« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»dali« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »dali«. If not, see <http://www.gnu.org/licenses/>.
*/
namespace _dali.widgets
{
/**
*/
export class class_widget_caldav
{
/**
*/
public constructor(
)
{
}
/**
*/
public async load(
target_element : HTMLElement
)
: Promise<void>
{
target_element.innerHTML = "";
const conf = await _dali.backend.user_dav_conf();
target_element.innerHTML = await _dali.helpers.template_coin(
"widget-caldav",
"main",
{
"title": lib_plankton.translate.get("widget.caldav.title"),
"content": (
(conf === null)
?
await _dali.helpers.template_coin(
"widget-caldav",
"unavailable",
{
"text": lib_plankton.translate.get("widget.caldav.unavailable"),
}
)
:
await _dali.helpers.template_coin(
"widget-caldav",
"available",
{
"conf_title": lib_plankton.translate.get("widget.caldav.conf.title"),
"conf_content": (
(conf.password === null)
?
await _dali.helpers.template_coin(
"widget-caldav",
"conf-token_unset",
{
"text": lib_plankton.translate.get("widget.caldav.conf.token_unset")
}
)
:
await _dali.helpers.template_coin(
"widget-caldav",
"conf-token_set",
{
"address_label": lib_plankton.translate.get("widget.caldav.conf.address"),
"address_value": conf.address,
"username_label": lib_plankton.translate.get("widget.caldav.conf.username"),
"username_value": conf.username,
"password_label": lib_plankton.translate.get("widget.caldav.conf.password"),
"password_value": conf.password,
"setup_hints_label": lib_plankton.translate.get("widget.caldav.conf.setup_hints"),
"setup_hint_entries": (
await lib_plankton.call.promise_condense<string, unknown>(
conf.setup_hints
.map(
entry => () => _dali.helpers.template_coin(
"widget-caldav",
"conf-setup_hint_entry",
{
"text": entry.label,
"href": entry.link,
"remark": (
(entry.remark === null)
?
""
:
lib_plankton.string.coin(
" — {{content}}",
{
"content": entry.remark,
}
)
)
}
)
)
)
).join("")
}
)
),
"set_token_title": lib_plankton.translate.get("widget.caldav.set_token.title"),
"set_token_action": (
(conf.password === null)
?
lib_plankton.translate.get("widget.caldav.set_token.action.set")
:
lib_plankton.translate.get("widget.caldav.set_token.action.overwrite")
),
}
)
),
"close": lib_plankton.translate.get("common.close"),
}
);
// logic
{
// set token
{
if (conf === null)
{
// do nothing
}
else
{
target_element.querySelector(".widget-caldav-set_token > button").addEventListener(
"click",
async () => {
await _dali.backend.user_dav_token();
await this.load(target_element);
}
);
}
}
// close
{
target_element.querySelector(".widget-caldav-close").addEventListener(
"click",
() => {
_dali.overlay.toggle({"mode": false});
}
);
}
}
// init
{
(target_element.querySelector(".widget-caldav-close") as HTMLElement).focus();
}
return Promise.resolve<void>(undefined);
}
}
}

View file

@ -1,9 +1,9 @@
.caldav-conf-section .widget-caldav-conf-section
{ {
margin-bottom: 16px; margin-bottom: 16px;
} }
.caldav-conf-section-label .widget-caldav-conf-section-label
{ {
margin-left: 16px; margin-left: 16px;
display: block; display: block;
@ -11,12 +11,12 @@
text-transform: capitalize; text-transform: capitalize;
} }
.caldav-conf-section-value .widget-caldav-conf-section-value
{ {
margin-left: 32px; margin-left: 32px;
} }
.caldav-conf-section-value-regular .widget-caldav-conf-section-value-regular
{ {
font-family: monospace; font-family: monospace;
} }

View file

@ -1,8 +1,8 @@
<div id="caldav-conf"> <div class="widget-caldav-conf">
<h3>{{conf_title}}</h3> <h3>{{conf_title}}</h3>
{{conf_content}} {{conf_content}}
</div> </div>
<div id="caldav-set_token"> <div class="widget-caldav-set_token">
<h3>{{set_token_title}}</h3> <h3>{{set_token_title}}</h3>
<button>{{set_token_action}}</button> <button>{{set_token_action}}</button>
</div> </div>

View file

@ -1,3 +1,3 @@
<li class="caldav-conf-setup_hints-entry"> <li class="widget-caldav-conf-setup_hints-entry">
<a target="_blank" href="{{href}}">{{text}}</a>{{remark}} <a target="_blank" href="{{href}}">{{text}}</a>{{remark}}
</li> </li>

View file

@ -0,0 +1,18 @@
<div class="widget-caldav-conf-section" class="widget-caldav-conf-address">
<span class="widget-caldav-conf-section-label">{{address_label}}</span>
<span class="widget-caldav-conf-section-value widget-caldav-conf-section-value-regular">{{address_value}}</span>
</div>
<div class="widget-caldav-conf-section" class="widget-caldav-conf-username">
<span class="widget-caldav-conf-section-label">{{username_label}}</span>
<span class="widget-caldav-conf-section-value widget-caldav-conf-section-value-regular">{{username_value}}</span>
</div>
<div class="widget-caldav-conf-section" class="widget-caldav-conf-password">
<span class="widget-caldav-conf-section-label">{{password_label}}</span>
<span class="widget-caldav-conf-section-value widget-caldav-conf-section-value-regular">{{password_value}}</span>
</div>
<div class="widget-caldav-conf-section" class="widget-caldav-conf-setup_hints">
<span class="widget-caldav-conf-section-label">{{setup_hints_label}}</span>
<ul>
{{setup_hint_entries}}
</ul>
</div>

View file

@ -0,0 +1,4 @@
<div class="widget-caldav-conf-info">
({{text}})
</div>

View file

@ -0,0 +1,8 @@
<div class="widget-caldav">
<h2>{{title}}</h2>
<div class="widget-caldav-content">
{{content}}
</div>
<hr/>
<button class="widget-caldav-close">{{close}}</button>
</div>

View file

@ -0,0 +1,3 @@
<div class="widget-caldav-info">
{{text}}
</div>

View file

@ -1,4 +1,24 @@
namespace _dali.widgets.calendar_edit /*
This file is part of »dali«.
Copyright 2025 'kcf' <fenris@folksprak.org>
»dali« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»dali« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »dali«. If not, see <http://www.gnu.org/licenses/>.
*/
namespace _dali.widgets
{ {
/** /**
@ -91,6 +111,13 @@ namespace _dali.widgets.calendar_edit
target_element : HTMLElement target_element : HTMLElement
) : Promise<void> ) : Promise<void>
{ {
const dom_root = await _dali.helpers.element_from_template(
"widget-calendar_edit",
"main",
{
}
);
const form : lib_plankton.zoo_form.class_form< const form : lib_plankton.zoo_form.class_form<
_dali.type_calendar_object, _dali.type_calendar_object,
_dali.type_calendar_object _dali.type_calendar_object
@ -223,9 +250,12 @@ namespace _dali.widgets.calendar_edit
) )
) )
); );
await form.setup(target_element); await form.setup(dom_root);
await form.input_lock(this.read_only); await form.input_lock(this.read_only);
await form.input_write(this.initial_value); await form.input_write(this.initial_value);
target_element.appendChild(dom_root);
form.input_focus();
} }
} }

View file

@ -0,0 +1,4 @@
.widget-calendar_edit .plankton_input_group_field[rel="resource"]
{
display: none;
}

View file

@ -0,0 +1,2 @@
<div class="widget-calendar_edit">
</div>

View file

@ -1,4 +1,24 @@
namespace _dali.widgets.event_edit /*
This file is part of »dali«.
Copyright 2025 'kcf' <fenris@folksprak.org>
»dali« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»dali« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »dali«. If not, see <http://www.gnu.org/licenses/>.
*/
namespace _dali.widgets
{ {
/** /**
@ -134,6 +154,13 @@ namespace _dali.widgets.event_edit
target_element : HTMLElement target_element : HTMLElement
) : Promise<void> ) : Promise<void>
{ {
const dom_root = await _dali.helpers.element_from_template(
"widget-event_edit",
"main",
{
}
);
const form : lib_plankton.zoo_form.class_form< const form : lib_plankton.zoo_form.class_form<
type_value, type_value,
type_representation type_representation
@ -296,9 +323,12 @@ namespace _dali.widgets.event_edit
) )
) )
); );
await form.setup(target_element); await form.setup(dom_root);
await form.input_lock(this.read_only); await form.input_lock(this.read_only);
await form.input_write(this.initial_value); await form.input_write(this.initial_value);
target_element.appendChild(dom_root);
form.input_focus();
} }
} }

View file

@ -0,0 +1,2 @@
<div class="widget-event_edit">
</div>

View file

@ -1,4 +1,24 @@
namespace _dali.widgets.listview /*
This file is part of »dali«.
Copyright 2025 'kcf' <fenris@folksprak.org>
»dali« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»dali« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »dali«. If not, see <http://www.gnu.org/licenses/>.
*/
namespace _dali.widgets
{ {
/** /**
@ -142,12 +162,12 @@ namespace _dali.widgets.listview
/** /**
* [implementation]
*/ */
public async load( public async update_entries(
target_element : Element
) )
: Promise<void> : Promise<void>
{
// structure
{ {
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;
@ -164,23 +184,8 @@ namespace _dali.widgets.listview
lib_plankton.pit.from_datetime(y.event_object.begin) lib_plankton.pit.from_datetime(y.event_object.begin)
) )
); );
const dom_list : HTMLElement = this.container.querySelector(".listview-entries");
// view dom_list.innerHTML = (
{
target_element.innerHTML = await _dali.helpers.template_coin(
"widget-listview",
"main",
{
"add_href": "",
"add_label": lib_plankton.translate.get("widget.listview.add"),
"add_extra_classes": (
(! await _dali.is_logged_in())
?
" listview-add-hidden"
:
""
),
"entries": (
( (
await _dali.helpers.promise_row<string>( await _dali.helpers.promise_row<string>(
entries entries
@ -267,21 +272,11 @@ namespace _dali.widgets.listview
) )
) )
.join("") .join("")
),
}
); );
} }
// listeners
// control
{ {
target_element.querySelector(".listview-add").addEventListener( this.container.querySelectorAll(".listview-entry").forEach(
"click",
(event) => {
event.preventDefault();
this.action_add();
}
);
target_element.querySelectorAll(".listview-entry").forEach(
(element) => { (element) => {
element.addEventListener( element.addEventListener(
"click", "click",
@ -300,8 +295,51 @@ namespace _dali.widgets.listview
} }
); );
} }
}
this.container = target_element.querySelector(".listview");
/**
* [implementation]
*/
public async load(
target_element : Element
)
: Promise<void>
{
this.container = target_element;
// view
{
target_element.innerHTML = await _dali.helpers.template_coin(
"widget-listview",
"main",
{
"add_href": "",
"add_label": lib_plankton.translate.get("widget.listview.add"),
"add_extra_classes": (
(! await _dali.is_logged_in())
?
" listview-add-hidden"
:
""
),
"entries": "",
}
);
}
// control
{
target_element.querySelector(".listview-add").addEventListener(
"click",
(event) => {
event.preventDefault();
this.action_add();
}
);
}
await this.update_entries();
return Promise.resolve<void>(undefined); return Promise.resolve<void>(undefined);
} }

View file

@ -1,4 +1,24 @@
namespace _dali.widgets.login /*
This file is part of »dali«.
Copyright 2025 'kcf' <fenris@folksprak.org>
»dali« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»dali« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »dali«. If not, see <http://www.gnu.org/licenses/>.
*/
namespace _dali.widgets
{ {
/** /**
@ -53,7 +73,7 @@ namespace _dali.widgets.login
* [implementation] * [implementation]
*/ */
public async load( public async load(
target_element : Element target_element : HTMLElement
) )
: Promise<void> : Promise<void>
{ {
@ -66,125 +86,36 @@ namespace _dali.widgets.login
{ {
case "internal": case "internal":
{ {
target_element.innerHTML = await _dali.helpers.template_coin( const sub_widget : lib_plankton.zoo_widget.interface_widget = (
"widget-login", new class_widget_login_internal(
"default", preparation.data,
{ {
"initial_name": this.initial_name,
"action_cancel": this.action_cancel,
"action_success": this.action_success,
} }
);
const form : lib_plankton.zoo_form.class_form<
{name : string; password : string;},
{name : string; password : string;}
> = new lib_plankton.zoo_form.class_form<
{name : string; password : string;},
{name : string; password : string;}
>(
x => x,
x => x,
new lib_plankton.zoo_input.class_input_group<
{name : string; password : string;}
>(
[
{
"name": "name",
"input": new lib_plankton.zoo_input.class_input_text(),
"label": lib_plankton.translate.get("widget.login.internal.name"),
},
{
"name": "password",
"input": new lib_plankton.zoo_input.class_input_password(),
"label": lib_plankton.translate.get("widget.login.internal.password"),
},
]
),
(
[]
.concat(
[
{
"label": lib_plankton.translate.get("widget.login.internal.do"),
"procedure": async (get_value, get_representation) => {
const value : any = await get_value();
try
{
await _dali.backend.session_begin(
value.name,
value.password
);
if (this.action_success !== null) this.action_success();
}
catch (error)
{
// todo
}
}
}
]
)
.concat(
(this.action_cancel === null)
?
[]
:
[
{
"label": lib_plankton.translate.get("common.cancel"),
"procedure": () => {
this.action_cancel();
}
}
]
)
) )
); );
await form.setup(document.querySelector(".widget-login")); await sub_widget.load(target_element);
await form.input_write(
{
"name": this.initial_name,
"password": "",
}
);
break; break;
} }
case "oidc": case "oidc":
{ {
// link const sub_widget : lib_plankton.zoo_widget.interface_widget = (
new class_widget_login_oidc(
preparation.data,
{ {
let element_a : HTMLElement = document.createElement("a");; "action_cancel": this.action_cancel,
element_a.textContent = lib_plankton.string.coin( "action_success": this.action_success,
lib_plankton.translate.get("widget.login.oidc.via"),
{
"title": preparation.data.label,
} }
)
); );
element_a.setAttribute("href", preparation.data.url); await sub_widget.load(target_element);
target_element.appendChild(element_a);
}
{
let dom_br : HTMLElement = document.createElement("br");
target_element.appendChild(dom_br);
}
{
let dom_br : HTMLElement = document.createElement("br");
target_element.appendChild(dom_br);
}
// cancel
{
let dom_cancel : HTMLElement = document.createElement("button");
dom_cancel.textContent = lib_plankton.translate.get("common.cancel");
dom_cancel.addEventListener(
"click",
() => {
this.action_cancel();
}
);
target_element.appendChild(dom_cancel);
}
break; break;
} }
default: default:
{ {
// todo throw (new Error("unhandled login kind: " + preparation.kind));
break; break;
} }
} }

View file

@ -1,2 +0,0 @@
<div class="widget-login">
</div>

View file

@ -0,0 +1,165 @@
/*
This file is part of »dali«.
Copyright 2025 'kcf' <fenris@folksprak.org>
»dali« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»dali« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »dali«. If not, see <http://www.gnu.org/licenses/>.
*/
namespace _dali.widgets
{
/**
*/
export class class_widget_login_internal
implements lib_plankton.zoo_widget.interface_widget
{
/**
*/
private preparation_data : any;
/**
*/
private initial_name : (null | string);
/**
* [hook]
*/
private action_cancel : (null | (() => void));
/**
* [hook]
*/
private action_success : (null | (() => void));
/**
*/
public constructor(
preparation_data : any,
{
"initial_name": initial_name = null,
"action_cancel": action_cancel = null,
"action_success": action_success = null,
}
:
{
initial_name ?: (null | string);
action_cancel ?: (null | (() => void));
action_success ?: (null | (() => void));
}
=
{
}
)
{
this.preparation_data = preparation_data;
this.initial_name = initial_name;
this.action_cancel = action_cancel;
this.action_success = action_success;
}
/**
* [implementation]
*/
public async load(
target_element : HTMLElement
)
: Promise<void>
{
const form : lib_plankton.zoo_form.class_form<
{name : string; password : string;},
{name : string; password : string;}
> = new lib_plankton.zoo_form.class_form<
{name : string; password : string;},
{name : string; password : string;}
>(
x => x,
x => x,
new lib_plankton.zoo_input.class_input_group<
{name : string; password : string;}
>(
[
{
"name": "name",
"input": new lib_plankton.zoo_input.class_input_text(),
"label": lib_plankton.translate.get("widget.login.internal.name"),
},
{
"name": "password",
"input": new lib_plankton.zoo_input.class_input_password(),
"label": lib_plankton.translate.get("widget.login.internal.password"),
},
]
),
(
[]
.concat(
[
{
"label": lib_plankton.translate.get("widget.login.internal.do"),
"procedure": async (get_value, get_representation) => {
const value : any = await get_value();
try
{
await _dali.backend.session_begin(
value.name,
value.password
);
if (this.action_success !== null) this.action_success();
}
catch (error)
{
// todo
}
}
}
]
)
.concat(
(this.action_cancel === null)
?
[]
:
[
{
"label": lib_plankton.translate.get("common.cancel"),
"procedure": () => {
this.action_cancel();
}
}
]
)
)
);
target_element.innerHTML = "";
await form.setup(target_element);
await form.input_write(
{
"name": this.initial_name,
"password": "",
}
);
form.input_focus();
}
}
}

View file

@ -0,0 +1,110 @@
/*
This file is part of »dali«.
Copyright 2025 'kcf' <fenris@folksprak.org>
»dali« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»dali« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »dali«. If not, see <http://www.gnu.org/licenses/>.
*/
namespace _dali.widgets
{
/**
*/
export class class_widget_login_oidc
implements lib_plankton.zoo_widget.interface_widget
{
/**
*/
private preparation_data : any;
/**
* [hook]
*/
private action_cancel : (null | (() => void));
/**
* [hook]
*/
private action_success : (null | (() => void));
/**
*/
public constructor(
preparation_data : any,
{
"action_cancel": action_cancel = null,
"action_success": action_success = null,
}
:
{
action_cancel ?: (null | (() => void));
action_success ?: (null | (() => void));
}
=
{
}
)
{
this.preparation_data = preparation_data;
this.action_cancel = action_cancel;
this.action_success = action_success;
}
/**
* [implementation]
*/
public async load(
target_element : HTMLElement
)
: Promise<void>
{
// structure
{
target_element.innerHTML = await _dali.helpers.template_coin(
"widget-login_oidc",
"main",
{
"label": lib_plankton.string.coin(
lib_plankton.translate.get("widget.login.oidc.via"),
{
"title": this.preparation_data.label,
}
),
"href": this.preparation_data.url,
"cancel": lib_plankton.translate.get("common.cancel"),
}
);
}
// controls
{
target_element.querySelector(".widget-login_oidc-cancel").addEventListener(
"click",
() => {
this.action_cancel();
}
);
}
}
}
}

View file

@ -0,0 +1,5 @@
.widget-login_oidc-cancel
{
display: block;
margin-top: 32px;
}

View file

@ -0,0 +1,4 @@
<div class="widget-login_oidc">
<a class="widget-login_oidc-link" href="{{href}}">{{label}}</a>
<button class="widget-login_oidc-cancel">{{cancel}}</button>
</div>

View file

@ -1,4 +1,24 @@
namespace _dali.widgets.menu /*
This file is part of »dali«.
Copyright 2025 'kcf' <fenris@folksprak.org>
»dali« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»dali« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »dali«. If not, see <http://www.gnu.org/licenses/>.
*/
namespace _dali.widgets
{ {
/** /**
@ -17,12 +37,7 @@ namespace _dali.widgets.menu
/** /**
*/ */
private entries : Array< private entries : Array<type_entry_data>;
{
data : type_entry_data;
element : (null | HTMLElement);
}
>;
/** /**
@ -43,10 +58,10 @@ namespace _dali.widgets.menu
/** /**
*/ */
public constructor( public constructor(
entry_data_list : Array<type_entry_data>, entries : Array<type_entry_data>,
{ {
"initial_groups": initial_groups = [], "initial_groups": initial_groups = [],
"initial_label": initial_label = "=", "initial_label": initial_label = "",
} }
: :
{ {
@ -58,14 +73,7 @@ namespace _dali.widgets.menu
} }
) )
{ {
this.entries = entry_data_list.map( this.entries = entries;
entry_data => (
{
"data": entry_data,
"element": null,
}
)
);
this.initial_groups = initial_groups; this.initial_groups = initial_groups;
this.label = initial_label; this.label = initial_label;
this.container = null; this.container = null;
@ -80,25 +88,28 @@ namespace _dali.widgets.menu
: void : void
{ {
this.entries.forEach( this.entries.forEach(
entry => { (entry, index) => {
const active : boolean = groups.some(group => entry.data.groups.includes(group)); const active : boolean = groups.some(group => entry.groups.includes(group));
entry.element.classList.toggle("widget-menu-entry-hidden", (! active)); const rel : string = index.toFixed(0);
const dom_entry = this.container.querySelector(".widget-menu-entry[rel=\"" + rel + "\"]");
dom_entry.classList.toggle("widget-menu-entry-hidden", (! active));
} }
); );
} }
/** /**
*/ */
public set_label( public set_label(
label ?: string label : (null | string)
) )
: void : void
{ {
this.label = label; this.label = label;
this.container.querySelector(".widget-menu-button").textContent = ( this.container.querySelector(".widget-menu-button").innerHTML = (
(this.label === null) (this.label === null)
? ?
"[=]" ("[" + "=" + "]")
: :
("[" + this.label + "]") ("[" + this.label + "]")
); );
@ -133,56 +144,69 @@ namespace _dali.widgets.menu
) )
: Promise<void> : Promise<void>
{ {
// container // structure
this.container = await _dali.helpers.element_from_template(
"widget-menu",
"main",
{ {
const dom_container : HTMLElement = document.createElement("div"); "entries": (
dom_container.classList.add("widget-menu"); (
// button await lib_plankton.call.promise_condense(
this.entries.map(
(entry, index) => () => _dali.helpers.template_coin(
"widget-menu",
"entry",
{ {
const dom_button : HTMLElement = document.createElement("button"); "label": entry.label,
dom_button.textContent = "[" + this.label + "]"; "rel": index.toFixed(0),
dom_button.classList.add("widget-menu-button"); }
dom_button.addEventListener( )
)
)
)
.join("")
),
}
);
// logic
{
// collapser
{
this.container.querySelector(".widget-menu-button").addEventListener(
"click", "click",
() => { () => {
this.toggle_collapsed(); this.toggle_collapsed();
} }
); );
dom_container.classList.toggle("widget-menu-collapsed", true);
dom_container.appendChild(dom_button);
} }
// platform // entries
{ {
const dom_platform : HTMLElement = document.createElement("div"); this.container.querySelectorAll(".widget-menu-entry").forEach(
dom_platform.classList.add("widget-menu-platform"); dom_entry => {
{
const dom_list : HTMLElement = document.createElement("ul");
dom_list.classList.add("widget-menu-entries");
this.entries.forEach(
entry => {
const dom_entry : HTMLElement = document.createElement("li");
dom_entry.classList.add("widget-menu-entry");
dom_entry.textContent = entry.data.label;
dom_entry.addEventListener( dom_entry.addEventListener(
"click", "click",
() => { () => {
const index : int = parseInt(dom_entry.getAttribute("rel"));
const entry : type_entry_data = this.entries[index];
this.toggle_collapsed({"mode": true}); this.toggle_collapsed({"mode": true});
entry.data.action(); entry.action();
} }
); );
dom_list.appendChild(dom_entry);
entry.element = dom_entry;
} }
); );
dom_platform.appendChild(dom_list);
} }
dom_container.appendChild(dom_platform);
}
target_element.appendChild(dom_container);
this.container = dom_container;
} }
// init
{
this.toggle_collapsed({"mode": true});
this.set_groups(this.initial_groups); this.set_groups(this.initial_groups);
this.set_label(null);
}
// finish
target_element.appendChild(this.container);
} }
} }

View file

@ -29,7 +29,9 @@
padding: 8px; padding: 8px;
/*
min-width: 200px; min-width: 200px;
*/
} }
.widget-menu-platform:not(.widget-menu-platform-collapsed) .widget-menu-platform:not(.widget-menu-platform-collapsed)
@ -49,23 +51,22 @@
.widget-menu-entry .widget-menu-entry
{ {
margin: 8px; margin: 12px 8px;
text-transform: capitalize; text-transform: capitalize;
}
.widget-menu-entry:not(:hover)
{
background-color: hsl(var(--hue), 0%, 25%); background-color: hsl(var(--hue), 0%, 25%);
color: hsl(var(--hue), 0%, 100%); color: hsl(var(--hue), 0%, 100%);
} }
.widget-menu-entry:hover::before .widget-menu-entry:not(:hover) > span
{ {
content: "» "; border-bottom: 2px solid hsl(0, 0%, 25%);
/* }
background-color: hsl(var(--hue), 0%, 50%);
color: hsl(var(--hue), 0%, 100%); .widget-menu-entry:hover > span
*/ {
border-bottom: 2px solid hsl(0, 0%, 100%);
transition: 1s ease color;
} }
.widget-menu-entry.widget-menu-entry-hidden .widget-menu-entry.widget-menu-entry-hidden

View file

@ -0,0 +1,3 @@
<li class="widget-menu-entry" rel="{{rel}}">
<span>{{label}}</span>
</li>

View file

@ -0,0 +1,8 @@
<div class="widget-menu">
<button class="widget-menu-button"></button>
<div class="widget-menu-platform">
<ul class="widget-menu-entries">
{{entries}}
</ul>
</div>
</div>

View file

@ -1,4 +1,24 @@
namespace _dali.widgets.mode_switcher /*
This file is part of »dali«.
Copyright 2025 'kcf' <fenris@folksprak.org>
»dali« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»dali« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »dali«. If not, see <http://www.gnu.org/licenses/>.
*/
namespace _dali.widgets
{ {
/** /**
@ -58,7 +78,8 @@ namespace _dali.widgets.mode_switcher
*/ */
public async load( public async load(
target_element : Element target_element : Element
) : Promise<void> )
: Promise<void>
{ {
target_element.innerHTML = await _dali.helpers.template_coin( target_element.innerHTML = await _dali.helpers.template_coin(
"widget-mode_switcher", "widget-mode_switcher",

View file

@ -0,0 +1,4 @@
.widget-mode_switcher-option
{
margin-left: 16px;
}

View file

@ -0,0 +1,278 @@
/*
This file is part of »dali«.
Copyright 2025 'kcf' <fenris@folksprak.org>
»dali« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»dali« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »dali«. If not, see <http://www.gnu.org/licenses/>.
*/
namespace _dali.widgets
{
/**
*/
type type_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>>
);
/**
*/
type type_create_event = (
(
stuff ?: {
date ?: lib_plankton.pit.type_date;
}
)
=>
Promise<void>
);
/**
*/
type type_edit_event = (
(
event_key : _dali.type_event_key
)
=>
Promise<void>
);
/**
*/
export class class_widget_multiview
implements lib_plankton.zoo_widget.interface_widget
{
/**
*/
private initial_view_mode : _dali.enum_view_mode;
/**
*/
private weekview_initial_vertical : boolean;
/**
*/
private get_entries : type_get_entries;
/**
*/
private action_create_event : type_create_event;
/**
*/
private action_edit_event : type_edit_event;
/**
*/
private dom_context : (null | HTMLElement);
/**
*/
private widget_mode_switcher : _dali.widgets.class_widget_mode_switcher;
/**
*/
private widget_weekview : _dali.widgets.class_widget_weekview;
/**
*/
private widget_listview : _dali.widgets.class_widget_listview;
/**
*/
public constructor(
get_entries : type_get_entries,
{
"initial_view_mode": initial_view_mode = _dali.enum_view_mode.week,
"weekview_initial_vertical": weekview_initial_vertical = false,
"action_create_event": action_create_event = ((stuff) => Promise.resolve<void>(undefined)),
"action_edit_event": action_edit_event = ((event_key) => Promise.resolve<void>(undefined)),
}
:
{
initial_view_mode ?: _dali.enum_view_mode;
weekview_initial_vertical ?: boolean;
action_create_event ?: type_create_event;
action_edit_event ?: type_edit_event;
}
=
{
}
)
{
this.get_entries = get_entries;
this.initial_view_mode = initial_view_mode;
this.weekview_initial_vertical = weekview_initial_vertical;
this.action_create_event = action_create_event;
this.action_edit_event = action_edit_event;
this.dom_context = null;
}
/**
*/
public toggle_calendar_visibilty(
calendar_id : _dali.type_calendar_id,
{
"mode": mode = null,
}
:
{
mode ?: (null | boolean);
}
=
{
}
)
: void
{
this.widget_weekview.toggle_visibility(calendar_id, {"mode": mode});
this.widget_listview.toggle_visibility(calendar_id, {"mode": mode});
}
/**
*/
private set_view_mode
(
view_mode : _dali.enum_view_mode
)
: void
{
this.dom_context.setAttribute("rel", _dali.view_mode_encode(view_mode));
}
/**
*/
public async update_entries(
)
:
Promise<void>
{
await Promise.all(
[
this.widget_weekview.update_entries(),
this.widget_listview.update_entries(),
]
);
}
/**
* [implementation]
*/
public async load(
target_element : Element
)
: Promise<void>
{
this.dom_context = (target_element as HTMLElement);
this.dom_context.classList.add("widget-multiview");
// mode switcher
{
this.widget_mode_switcher = (
new _dali.widgets.class_widget_mode_switcher(
[
{
"mode": _dali.enum_view_mode.week,
/**
* @todo as dependency
*/
"label": lib_plankton.translate.get("widget.overview.mode.week"),
},
{
"mode": _dali.enum_view_mode.list,
/**
* @todo as dependency
*/
"label": lib_plankton.translate.get("widget.overview.mode.list"),
},
],
{
"initial_selection": this.initial_view_mode,
"action_change": (view_mode) => {
this.set_view_mode(view_mode);
}
}
)
);
let dom_wrapper = document.createElement("div");
dom_wrapper.classList.add("widget-multiview-mode_switcher");
await this.widget_mode_switcher.load(dom_wrapper);
this.dom_context.appendChild(dom_wrapper);
}
// weekview
{
this.widget_weekview = (
new _dali.widgets.class_widget_weekview(
this.get_entries,
{
"action_select_event": (event_key) => this.action_edit_event(event_key),
"action_select_day": (date) => this.action_create_event({"date": date}),
"vertical": this.weekview_initial_vertical,
}
)
);
let dom_wrapper = document.createElement("div");
dom_wrapper.classList.add("widget-multiview-weekview");
await this.widget_weekview.load(dom_wrapper);
this.dom_context.appendChild(dom_wrapper);
}
// listview
{
this.widget_listview = (
new _dali.widgets.class_widget_listview(
this.get_entries,
{
"action_select": (event_key) => this.action_edit_event(event_key),
"action_add": () => this.action_create_event(),
}
)
);
let dom_wrapper = document.createElement("div");
dom_wrapper.classList.add("widget-multiview-listview");
await this.widget_listview.load(dom_wrapper);
this.dom_context.appendChild(dom_wrapper);
}
this.set_view_mode(this.initial_view_mode);
return Promise.resolve<void>(undefined);
}
}
}

View file

@ -0,0 +1,10 @@
.widget-multiview-mode_switcher
{
margin-bottom: 12px;
}
.widget-multiview[rel="week"] > .widget-multiview-weekview {}
.widget-multiview[rel="week"] > .widget-multiview-listview {display: none;}
.widget-multiview[rel="list"] > .widget-multiview-weekview {display: none;}
.widget-multiview[rel="list"] > .widget-multiview-listview {}

View file

@ -1,141 +1,115 @@
namespace _dali.pages.overview
{
/**
*/
lib_plankton.zoo_page.register(
"overview",
async (parameters, target_element) => {
// params
const view_mode : _dali.enum_view_mode = _dali.helpers.view_mode_determine(parameters["mode"] ?? "auto");
// exec
target_element.innerHTML = await _dali.helpers.template_coin(
"overview",
"default",
{
}
);
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);
}
/**
*/
async function get_available_calendars(
)
: Promise<
Array<
_dali.type_calendar_object_reduced_with_id
>
>
{
return (
(await _dali.model.calendar_list())
/* /*
.filter( This file is part of »dali«.
(entry) => (
(entry.access_level === _dali.enum_access_level.edit) Copyright 2025 'kcf' <fenris@folksprak.org>
||
(entry.access_level === _dali.enum_access_level.admin) »dali« is free software: you can redistribute it and/or modify
) it under the terms of the GNU Lesser General Public License as published by
) the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»dali« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »dali«. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace _dali.widgets
{
/**
*/
type type_action_create_calendar = (
(
)
=>
Promise<void>
); );
}
/** /**
* @todo add return type
*/ */
async function get_entries( type type_action_edit_calendar = (
from_pit : lib_plankton.pit.type_pit, (
to_pit : lib_plankton.pit.type_pit, type_calendar_object_reduced_with_id
calendar_ids : Array<_dali.type_calendar_id>
) )
: Promise<Array<_dali.type_event_object_extended>> =>
{ Promise<void>
/**
* @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( type type_action_create_event = (
(
stuff ?: {
date ?: lib_plankton.pit.type_date;
}
) )
: Promise<void> =>
Promise<void>
);
/**
*/
type type_action_edit_event = (
(
event_key : _dali.type_event_key
)
=>
Promise<void>
);
/**
*/
export class class_widget_overview
implements lib_plankton.zoo_widget.interface_widget
{ {
const widget = new _dali.widgets.calendar_edit.class_widget_calendar_edit(
/**
*/
private widget_sources : _dali.widgets.class_widget_sources;
/**
*/
private widget_multiview : _dali.widgets.class_widget_multiview;
/**
*/
private action_create_calendar : type_action_create_calendar;
/**
*/
private action_edit_calendar : type_action_edit_calendar;
/**
*/
private action_create_event : type_action_create_event;
/**
*/
private action_edit_event : type_action_edit_event;
/**
*/
public constructor(
)
{
this.action_create_calendar = async (
) => {
const widget = new _dali.widgets.class_widget_calendar_edit(
await _dali.model.group_list(), await _dali.model.group_list(),
await _dali.model.user_list(), await _dali.model.user_list(),
{ {
@ -143,7 +117,7 @@ namespace _dali.pages.overview
"hue": lib_plankton.random.generate_unit(), "hue": lib_plankton.random.generate_unit(),
"access": { "access": {
"public": false, "public": false,
"default_level": _dali.enum_access_level.view, "default_level": _dali.enum_access_level.none,
"attributed_group": lib_plankton.map.hashmap.implementation_map< "attributed_group": lib_plankton.map.hashmap.implementation_map<
_dali.type_group_id, _dali.type_group_id,
_dali.enum_access_level _dali.enum_access_level
@ -183,7 +157,7 @@ namespace _dali.pages.overview
) )
.then( .then(
() => { () => {
update_sources_and_entries(); this.update_sources_and_entries();
_dali.overlay.toggle({"mode": false}); _dali.overlay.toggle({"mode": false});
} }
) )
@ -201,15 +175,10 @@ namespace _dali.pages.overview
_dali.overlay.clear(); _dali.overlay.clear();
_dali.overlay.toggle({"mode": true}); _dali.overlay.toggle({"mode": true});
await widget.load(_dali.overlay.get_content_element()); await widget.load(_dali.overlay.get_content_element());
} };
this.action_edit_calendar = async (
/** calendar_object_reduced_with_id
*/ ) => {
async function action_edit_calendar(
calendar_object_reduced_with_id : type_calendar_object_reduced_with_id
)
: Promise<void>
{
const read_only : boolean = (() => { const read_only : boolean = (() => {
switch (calendar_object_reduced_with_id.access_level) switch (calendar_object_reduced_with_id.access_level)
{ {
@ -246,7 +215,7 @@ namespace _dali.pages.overview
const calendar_object : _dali.type_calendar_object = await _dali.model.calendar_get( const calendar_object : _dali.type_calendar_object = await _dali.model.calendar_get(
calendar_id calendar_id
); );
const widget = new _dali.widgets.calendar_edit.class_widget_calendar_edit( const widget = new _dali.widgets.class_widget_calendar_edit(
await _dali.model.group_list(), await _dali.model.group_list(),
await _dali.model.user_list(), await _dali.model.user_list(),
calendar_object, calendar_object,
@ -262,7 +231,7 @@ namespace _dali.pages.overview
) )
.then( .then(
() => { () => {
update_sources_and_entries(); this.update_sources_and_entries();
_dali.overlay.toggle({"mode": false}); _dali.overlay.toggle({"mode": false});
} }
); );
@ -273,7 +242,7 @@ namespace _dali.pages.overview
) )
.then( .then(
() => { () => {
update_sources_and_entries(); this.update_sources_and_entries();
_dali.overlay.toggle({"mode": false}); _dali.overlay.toggle({"mode": false});
} }
); );
@ -285,11 +254,10 @@ namespace _dali.pages.overview
await widget.load(_dali.overlay.get_content_element()); await widget.load(_dali.overlay.get_content_element());
} }
} }
/** /**
* @todo unterschiedliches Verhalten bei Anmeldung? * @todo unterschiedliches Verhalten bei Anmeldung?
*/ */
async function action_create_event( this.action_create_event = async (
{ {
"date": date = lib_plankton.pit.to_datetime(lib_plankton.pit.now()).date, "date": date = lib_plankton.pit.to_datetime(lib_plankton.pit.now()).date,
} }
@ -300,17 +268,15 @@ namespace _dali.pages.overview
= =
{ {
} }
) ) => {
: Promise<void>
{
if (! _dali.is_logged_in()) if (! _dali.is_logged_in())
{ {
// do nothing // do nothing
} }
else else
{ {
const widget = new _dali.widgets.event_edit.class_widget_event_edit( const widget = new _dali.widgets.class_widget_event_edit(
(await get_available_calendars()), (await this.get_available_calendars()),
{ {
"calendar_id": null, "calendar_id": null,
"event_name": "", "event_name": "",
@ -363,7 +329,7 @@ namespace _dali.pages.overview
) )
.then( .then(
() => { () => {
update_entries(); this.update_entries();
_dali.overlay.toggle({"mode": false}); _dali.overlay.toggle({"mode": false});
} }
) )
@ -380,15 +346,11 @@ namespace _dali.pages.overview
await widget.load(_dali.overlay.get_content_element()); await widget.load(_dali.overlay.get_content_element());
} }
} }
/** /**
*/ */
async function action_edit_event this.action_edit_event = async (
( event_key
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 event_object_extended : _dali.type_event_object_extended = await _dali.model.event_get(event_key);
const read_only : boolean = (() => { const read_only : boolean = (() => {
switch (event_object_extended.access_level) switch (event_object_extended.access_level)
@ -411,8 +373,8 @@ namespace _dali.pages.overview
} }
} }
}) (); }) ();
const widget = new _dali.widgets.event_edit.class_widget_event_edit( const widget = new _dali.widgets.class_widget_event_edit(
(await get_available_calendars()), (await this.get_available_calendars()),
{ {
"calendar_id": event_object_extended.calendar_id, "calendar_id": event_object_extended.calendar_id,
"event_name": event_object_extended.event_object.name, "event_name": event_object_extended.event_object.name,
@ -441,7 +403,7 @@ namespace _dali.pages.overview
) )
.then( .then(
() => { () => {
update_entries(); this.update_entries();
_dali.overlay.toggle({"mode": false}); _dali.overlay.toggle({"mode": false});
} }
) )
@ -460,7 +422,7 @@ namespace _dali.pages.overview
) )
.then( .then(
() => { () => {
update_entries(); this.update_entries();
_dali.overlay.toggle({"mode": false}); _dali.overlay.toggle({"mode": false});
} }
) )
@ -475,105 +437,212 @@ namespace _dali.pages.overview
}, },
} }
); );
_dali.overlay.clear(); _dali.overlay.clear();
_dali.overlay.toggle({"mode": true}); _dali.overlay.toggle({"mode": true});
await widget.load(_dali.overlay.get_content_element()); 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
/**
*/
private async get_available_calendars(
)
: Promise<
Array<
_dali.type_calendar_object_reduced_with_id
>
>
{ {
widget_mode_switcher = new _dali.widgets.mode_switcher.class_widget_mode_switcher( return (
[ (await _dali.model.calendar_list())
/*
.filter(
(entry) => (
(entry.access_level === _dali.enum_access_level.edit)
||
(entry.access_level === _dali.enum_access_level.admin)
)
)
*/
);
}
/**
*/
private async 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>>
{ {
"mode": _dali.enum_view_mode.week, /**
"label": lib_plankton.translate.get("page.overview.mode.week"), * @todo do NOT wait?
*/
await _dali.model.sync_events(
{
"from": from_pit,
"to": to_pit,
}, },
{ {
"mode": _dali.enum_view_mode.list, "calendar_ids": calendar_ids,
"label": lib_plankton.translate.get("page.overview.mode.list"),
},
],
{
"initial_selection": view_mode,
"action_change": (view_mode) => {
lib_plankton.zoo_page.set(
{
"name": "overview",
"parameters": {
"mode": _dali.view_mode_encode(view_mode),
}
} }
); );
set_view_mode(view_mode); /**
} * @todo filter
} */
); return _dali.model.event_list();
set_view_mode(view_mode);
await widget_mode_switcher.load(target_element.querySelector("#overview-mode"));
} }
// sources
/**
*/
private async update_sources_and_entries(
{ {
widget_sources = new _dali.widgets.sources.class_widget_sources( "priviliged": priviliged = null,
}
:
{
priviliged ?: (null | boolean);
}
=
{
}
)
: Promise<void>
{
await this.widget_sources.update({"priviliged": priviliged});
await this.widget_multiview.update_entries();
}
/**
* @todo use priviliged?
*/
private async update_entries(
{
"priviliged": priviliged = null,
}
:
{
priviliged ?: (null | boolean);
}
=
{
}
)
: Promise<void>
{
await this.widget_multiview.update_entries();
}
/**
* [implementation]
*/
public async load(
target_element : HTMLElement
)
: Promise<void>
{
const view_mode : _dali.enum_view_mode = _dali.helpers.view_mode_determine("auto");
const view_kind : _dali.enum_view_kind = _dali.helpers.view_kind_determine("auto");
this.widget_sources = (
new _dali.widgets.class_widget_sources(
_dali.model.calendar_list, _dali.model.calendar_list,
{ {
"initial_priviliged": _dali.is_logged_in(), "initial_priviliged": _dali.is_logged_in(),
"action_add": action_create_calendar, "action_add": this.action_create_calendar,
"action_select": (entry) => action_edit_calendar(entry), "action_select": this.action_edit_calendar,
"action_toggle": (entry, mode) => { "action_toggle": (entry, mode) => {
widget_weekview.toggle_visibility(entry.id, {"mode": mode}); this.widget_multiview.toggle_calendar_visibilty(entry.id, {"mode": mode});
widget_listview.toggle_visibility(entry.id, {"mode": mode});
}, },
} }
)
); );
await widget_sources.load(target_element.querySelector("#overview-pane-left")); this.widget_multiview = (
} new _dali.widgets.class_widget_multiview(
this.get_entries,
// weekview
{ {
widget_weekview = ( "initial_view_mode": view_mode,
new _dali.widgets.weekview.class_widget_weekview( "weekview_initial_vertical": (view_kind === _dali.enum_view_kind.touch),
get_entries, "action_create_event": this.action_create_event,
{ "action_edit_event": this.action_edit_event,
"action_select_event": (event_key) => action_edit_event(event_key),
"action_select_day": (date) => action_create_event({"date": date}),
} }
) )
); );
await widget_weekview.load(target_element.querySelector("#overview-pane-right-weekview"));
}
// listview target_element.innerHTML = await _dali.helpers.template_coin(
"widget-overview",
"main",
{ {
widget_listview = (
new _dali.widgets.listview.class_widget_listview(
get_entries,
{
"action_select": (event_key) => action_edit_event(event_key),
"action_add": () => action_create_event(),
} }
)
); );
await widget_listview.load(target_element.querySelector("#overview-pane-right-listview"));
// head
{
// hint
{
const dom_hint = target_element.querySelector(".widget-overview-hint");
dom_hint.textContent = lib_plankton.translate.get("widget.overview.login_hint");
dom_hint.classList.toggle("widget-overview-hint-hidden", _dali.is_logged_in());
}
}
// body
{
let widget_slider : lib_plankton.zoo_widget.interface_widget;
switch (view_kind)
{
case _dali.enum_view_kind.regular:
{
widget_slider = new lib_plankton.zoo_widget.class_slider(
[
new lib_plankton.zoo_widget.class_bunch(
[
this.widget_sources,
this.widget_multiview,
]
),
],
{
"threshold": 100,
"initial_index": 1,
}
);
break;
}
case _dali.enum_view_kind.touch:
{
widget_slider = new lib_plankton.zoo_widget.class_slider(
[
this.widget_sources,
this.widget_multiview,
],
{
"threshold": 100,
"initial_index": 2,
}
);
break;
}
}
await widget_slider.load(target_element.querySelector(".widget-overview-body"));
} }
_dali.model.listen_reset( _dali.model.listen_reset(
async (priviliged) => { async (priviliged) => {
update_sources_and_entries({"priviliged": priviliged}); this.update_sources_and_entries({"priviliged": priviliged});
target_element.querySelector("#overview-hint").classList.toggle("overview-hint-hidden", priviliged); target_element.querySelector(".widget-overview-hint").classList.toggle("widget-overview-hint-hidden", priviliged);
} }
); );
return Promise.resolve<void>(undefined); return Promise.resolve<void>(undefined);
}, }
);
}
} }

View file

@ -0,0 +1,59 @@
.widget-overview-head
{
padding-bottom: 12px;
margin-bottom: 12px;
border-bottom: 1px solid;
}
.widget-overview-hint
{
text-align: center;
font-weight: bold;
}
.widget-overview-hint.widget-overview-hint-hidden
{
display: none;
}
.widget-overview-body
{
display: flex;
flex-direction: row;
flex-wrap: nowrap;
}
.widget-overview-pane-left
{
flex-basis: 12.5%;
flex-grow: 0;
flex-shrink: 1;
}
.widget-overview-pane-right
{
flex-basis: 87.5%;
flex-grow: 1;
flex-shrink: 1;
}
.widget-overview-body .widget-slider-slide .widget-bunch
{
display: flex;
flex-direction: row;
flex-wrap: nowrap;
}
.widget-overview-body .widget-slider-slide .widget-bunch-element:nth-child(1)
{
flex-grow: 0;
flex-shrink: 1;
flex-basis: 12.5%;
}
.widget-overview-body .widget-slider-slide .widget-bunch-element:nth-child(2)
{
flex-grow: 1;
flex-shrink: 1;
flex-basis: 87.5%;
}

View file

@ -0,0 +1,8 @@
<div class="widget-overview">
<div class="widget-overview-head">
<div class="widget-overview-hint">
</div>
</div>
<div class="widget-overview-body">
</div>
</div>

View file

@ -1,4 +1,24 @@
namespace _dali.widgets.sources /*
This file is part of »dali«.
Copyright 2025 'kcf' <fenris@folksprak.org>
»dali« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»dali« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »dali«. If not, see <http://www.gnu.org/licenses/>.
*/
namespace _dali.widgets
{ {
/** /**

View file

@ -0,0 +1,167 @@
/*
This file is part of »dali«.
Copyright 2025 'kcf' <fenris@folksprak.org>
»dali« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»dali« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »dali«. If not, see <http://www.gnu.org/licenses/>.
*/
namespace _dali.widgets
{
/**
*/
export class class_widget_special_number_input
implements lib_plankton.zoo_widget.interface_widget
{
/**
*/
private action_change : ((int) => Promise<void>);
/**
*/
private label : (null | string);
/**
*/
private minimum : (null | int);
/**
*/
private maximum : (null | int);
/**
*/
private value : int;
/**
*/
public constructor(
{
"label": label = null,
"minimum": minimum = null,
"maximum": maximum = null,
"initial_value": initial_value = 0,
"action_change": action_change = ((value) => Promise.resolve<void>(undefined)),
}
:
{
label ?: (null | string);
minimum ?: (null | int);
maximum ?: (null | int);
initial_value ?: int;
action_change ?: ((int) => Promise<void>);
}
=
{
}
)
{
this.label = label;
this.minimum = minimum;
this.maximum = maximum;
this.value = initial_value;
this.action_change = action_change;
}
/**
* [implementation]
*/
public async load(
target_element : HTMLElement
)
: Promise<void>
{
const dom_root = await _dali.helpers.element_from_template(
"widget-special_number_input",
"main",
{
"label": this.label,
}
);
const dom_input : HTMLInputElement = (dom_root.querySelector(".widget-special_number_input-input") as HTMLInputElement);
// listeners
{
dom_input.addEventListener(
"change",
() => {
const value : int = parseInt(dom_input.value);
if (
((this.minimum === null) || (value >= this.minimum))
&&
((this.maximum === null) || (value <= this.maximum))
)
{
this.value = value;
this.action_change(this.value);
}
else
{
// do nothing
}
}
);
dom_root.querySelector(".widget-special_number_input-prev").addEventListener(
"click",
() => {
if ((this.minimum === null) || (this.value > this.minimum))
{
this.value -= 1;
dom_input.value = this.value.toFixed(0);
this.action_change(this.value);
}
else
{
// do nothing
}
}
);
dom_root.querySelector(".widget-special_number_input-next").addEventListener(
"click",
() => {
if ((this.maximum === null) || (this.value < this.maximum))
{
this.value += 1;
dom_input.value = this.value.toFixed(0);
this.action_change(this.value);
}
else
{
// do nothing
}
}
);
}
// content
{
dom_input.value = this.value.toFixed(0);
}
target_element.appendChild(dom_root);
}
}
}

View file

@ -0,0 +1,34 @@
.widget-special_number_input-controls
{
white-space: nowrap;
}
.widget-special_number_input-controls > *
{
display: inline-block;
}
.widget-special_number_input-button
{
padding: 4px 4px;
margin: 0;
cursor: pointer;
}
.widget-special_number_input-label
{
display: block;
font-size: 0.75em;
/*
text-transform: uppercase;
*/
}
.widget-special_number_input-input
{
max-width: 40px;
margin: 0;
text-align: center;
}

View file

@ -0,0 +1,8 @@
<div class="widget-special_number_input">
<label class="widget-special_number_input-label">{{label}}</label>
<div class="widget-special_number_input-controls">
<div class="widget-special_number_input-button widget-special_number_input-prev">&#x25C2;</div>
<input type="text" class="widget-special_number_input-input" pattern="-?[0-9]+"/>
<div class="widget-special_number_input-button widget-special_number_input-next">&#x25B8;</div>
</div>
</div>

View file

@ -1,4 +1,24 @@
namespace _dali.widgets.weekview /*
This file is part of »dali«.
Copyright 2025 'kcf' <fenris@folksprak.org>
»dali« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»dali« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »dali«. If not, see <http://www.gnu.org/licenses/>.
*/
namespace _dali.widgets
{ {
/** /**
@ -16,7 +36,8 @@ namespace _dali.widgets.weekview
/** /**
*/ */
export class class_widget_weekview implements lib_plankton.zoo_widget.interface_widget export class class_widget_weekview
implements lib_plankton.zoo_widget.interface_widget
{ {
/** /**
@ -101,7 +122,7 @@ namespace _dali.widgets.weekview
"vertical": vertical = false, "vertical": vertical = false,
"initial_year": initial_year = null, "initial_year": initial_year = null,
"initial_week": initial_week = null, "initial_week": initial_week = null,
"initial_count": initial_count = 4, "initial_count": initial_count = 5,
} }
: :
{ {
@ -171,7 +192,8 @@ namespace _dali.widgets.weekview
*/ */
private static entry_hash( private static entry_hash(
entry : _dali.type_event_object_extended entry : _dali.type_event_object_extended
) : string )
: string
{ {
return lib_plankton.call.convey( return lib_plankton.call.convey(
{ {
@ -193,7 +215,8 @@ namespace _dali.widgets.weekview
private static event_generate_tooltip( private static event_generate_tooltip(
calendar_name : string, calendar_name : string,
event_object : _dali.type_event_object event_object : _dali.type_event_object
) : string )
: string
{ {
return ( return (
lib_plankton.string.coin( lib_plankton.string.coin(
@ -339,7 +362,8 @@ namespace _dali.widgets.weekview
*/ */
private async entry_insert( private async entry_insert(
entry : _dali.type_event_object_extended 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(
".weekview-cell[rel=\"{{rel}}\"] > .weekview-events", ".weekview-cell[rel=\"{{rel}}\"] > .weekview-events",
@ -360,19 +384,11 @@ namespace _dali.widgets.weekview
} }
else else
{ {
let dom_dummy : HTMLElement = document.createElement("div"); const dom_entry = await _dali.helpers.element_from_template(
dom_dummy.innerHTML = await _dali.helpers.template_coin(
"widget-weekview", "widget-weekview",
"tableview-cell-entry", "tableview-cell-entry",
{ {
"color": _dali.helpers.event_color(entry.hue), "color": _dali.helpers.event_color(entry.hue),
/*
"title": class_widget_weekview.event_generate_tooltip(
entry.calendar_name,
entry.event_object
),
*/
"title": "",
"name": entry.event_object.name, "name": entry.event_object.name,
"rel": entry.key, "rel": entry.key,
"additional_classes": lib_plankton.string.coin( "additional_classes": lib_plankton.string.coin(
@ -383,7 +399,6 @@ namespace _dali.widgets.weekview
), ),
} }
); );
const dom_entry : HTMLElement = dom_dummy.querySelector(".weekview-event_entry");
// listener // listener
dom_entry.addEventListener( dom_entry.addEventListener(
@ -409,7 +424,8 @@ namespace _dali.widgets.weekview
*/ */
private async entry_add( private async entry_add(
entry : _dali.type_event_object_extended 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);
if (dom_entry === null) if (dom_entry === null)
@ -434,7 +450,8 @@ namespace _dali.widgets.weekview
private async entry_update( private async entry_update(
key : _dali.type_event_key, key : _dali.type_event_key,
entry : _dali.type_event_object_extended entry : _dali.type_event_object_extended
) : Promise<void> )
: Promise<void>
{ {
if (! this.event_map.has(key)) if (! this.event_map.has(key))
{ {
@ -490,7 +507,8 @@ namespace _dali.widgets.weekview
*/ */
private async entry_remove( private async entry_remove(
key : _dali.type_event_key key : _dali.type_event_key
) : Promise<void> )
: Promise<void>
{ {
if (! this.event_map.has(key)) if (! this.event_map.has(key))
{ {
@ -519,7 +537,8 @@ namespace _dali.widgets.weekview
/** /**
*/ */
public async update_entries( public async update_entries(
) : Promise<void> )
: Promise<void>
{ {
const entries : Array<_dali.type_event_object_extended> = await this.get_entries_wrapped( const entries : Array<_dali.type_event_object_extended> = await this.get_entries_wrapped(
); );
@ -557,23 +576,11 @@ namespace _dali.widgets.weekview
} }
/**
*/
private async update_controls(
) : Promise<void>
{
const context : Element = this.container;
(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-count input") as HTMLInputElement).value = this.count.toFixed(0);
(context.querySelector(".weekview-control-vertical input") as HTMLInputElement).checked = this.vertical;
}
/** /**
*/ */
private async update_table( private async update_table(
) : Promise<void> )
: Promise<void>
{ {
/** /**
* @todo avoid? * @todo avoid?
@ -743,6 +750,8 @@ namespace _dali.widgets.weekview
today_begin_pit, today_begin_pit,
today_end_pit today_end_pit
); );
const day_as_date : lib_plankton.pit.type_date = lib_plankton.pit.to_datetime_ce(day_pit).date;
const day_as_ywd : lib_plankton.pit.type_ywd = lib_plankton.pit.to_ywd(day_pit);
return _dali.helpers.template_coin( return _dali.helpers.template_coin(
"widget-weekview", "widget-weekview",
"tableview-cell", "tableview-cell",
@ -752,48 +761,23 @@ namespace _dali.widgets.weekview
.concat(is_today ? ["weekview-cell-today"] : []) .concat(is_today ? ["weekview-cell-today"] : [])
.join(" ") .join(" ")
), ),
"title": lib_plankton.call.convey( "day": (
day_pit, _dali.conf.get().misc.weekview_cell_day_format
[ .replace(new RegExp("Y", "g"), day_as_date.year.toFixed(0).padStart(4, "0"))
lib_plankton.pit.to_datetime_ce, .replace(new RegExp("m", "g"), day_as_date.month.toFixed(0).padStart(2, "0"))
(x : lib_plankton.pit.type_datetime) => lib_plankton.string.coin( .replace(new RegExp("b", "g"), _dali.helpers.month_name(day_as_date.month))
.replace(new RegExp("d", "g"), day_as_date.day.toFixed(0).padStart(2, "0"))
.replace(new RegExp("W", "g"), day_as_ywd.week.toFixed(0).padStart(2, "0"))
.replace(new RegExp("w", "g"), day_as_ywd.day.toFixed(0).padStart(1, "0"))
),
"rel": lib_plankton.string.coin(
"{{year}}-{{month}}-{{day}}", "{{year}}-{{month}}-{{day}}",
{ {
"year": x.date.year.toFixed(0).padStart(4, "0"), "year": day_as_date.year.toFixed(0).padStart(4, "0"),
"month": x.date.month.toFixed(0).padStart(2, "0"), "month": day_as_date.month.toFixed(0).padStart(2, "0"),
"day": x.date.day.toFixed(0).padStart(2, "0"), "day": day_as_date.day.toFixed(0).padStart(2, "0"),
} }
), ),
]
),
"day": lib_plankton.call.convey(
day_pit,
[
lib_plankton.pit.to_datetime_ce,
(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"),
"day": x.date.day.toFixed(0).padStart(2, "0"),
}
),
]
),
"rel": lib_plankton.call.convey(
day_pit,
[
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": "" "entries": ""
} }
); );
@ -815,7 +799,11 @@ namespace _dali.widgets.weekview
element.addEventListener( element.addEventListener(
"click", "click",
(event) => { (event) => {
if (! (element === event.target)) if (
(! (element === event.target))
&&
(! (event.target as HTMLElement).classList.contains("weekview-day"))
)
{ {
// do nothing // do nothing
} }
@ -852,7 +840,8 @@ namespace _dali.widgets.weekview
= =
{ {
} }
) : void )
: void
{ {
this.container.querySelectorAll(".weekview-event_entry").forEach( this.container.querySelectorAll(".weekview-event_entry").forEach(
(element) => { (element) => {
@ -880,175 +869,97 @@ namespace _dali.widgets.weekview
*/ */
public async load( public async load(
target_element : Element target_element : Element
) : Promise<void> )
: Promise<void>
{ {
target_element.innerHTML = await _dali.helpers.template_coin( target_element.innerHTML = await _dali.helpers.template_coin(
"widget-weekview", "widget-weekview",
"main", "main",
{ {
"label_control_year": lib_plankton.translate.get("widget.weekview.controls.year"),
"label_control_week": lib_plankton.translate.get("widget.weekview.controls.week"),
"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"),
} }
); );
this.container = target_element.querySelector(".weekview"); // control:year
// controls
{ {
// direct inputs const widget : lib_plankton.zoo_widget.interface_widget = new _dali.widgets.class_widget_special_number_input(
{ {
[ "label": lib_plankton.translate.get("widget.weekview.controls.year"),
{ "minimum": 1900,
"name": "year", "maximum": 2500,
"selector": ".weekview-control-year input", "initial_value": this.year,
"retrieve": element => parseInt(element.value), "action_change": async (value) => {
"write": x => {this.year = x;} this.year = value;
await this.update_table();
await this.update_entries();
}, },
}
);
await widget.load(target_element.querySelector(".weekview-controls"));
}
// control:week
{ {
"name": "week", const widget : lib_plankton.zoo_widget.interface_widget = new _dali.widgets.class_widget_special_number_input(
"selector": ".weekview-control-week input",
"retrieve": element => parseInt(element.value),
"write": x => {this.week = x;}
},
{ {
"name": "count", "label": lib_plankton.translate.get("widget.weekview.controls.week"),
"selector": ".weekview-control-count input", "minimum": 1,
"retrieve": element => parseInt(element.value), /**
"write": x => {this.count = x;} * @todo correct
*/
"maximum": 53,
"initial_value": this.week,
"action_change": async (value) => {
this.week = value;
await this.update_table();
await this.update_entries();
}, },
}
);
await widget.load(target_element.querySelector(".weekview-controls"));
}
// control:count
/*
{ {
"name": "vertical", const widget : lib_plankton.zoo_widget.interface_widget = new _dali.widgets.class_widget_special_number_input(
"selector": ".weekview-control-vertical input", {
"retrieve": element => element.checked, "label": lib_plankton.translate.get("widget.weekview.controls.count"),
"write": x => {this.vertical = x;} "minimum": 2,
"maximum": 6,
"initial_value": this.count,
"action_change": async (value) => {
this.count = value;
await this.update_table();
await this.update_entries();
}, },
].forEach( }
(entry) => { );
const element : HTMLInputElement = (target_element.querySelector(entry.selector) as HTMLInputElement); await widget.load(target_element.querySelector(".weekview-controls"));
element.addEventListener( }
*/
// control:vertical
{
const dom_control = await _dali.helpers.element_from_template(
"widget-weekview",
"control-vertical",
{
"label": lib_plankton.translate.get("widget.weekview.controls.vertical"),
}
);
const dom_input : HTMLInputElement = (dom_control.querySelector("input") as HTMLInputElement);
dom_input.addEventListener(
"change", "change",
async (event) => { async () => {
event.preventDefault(); event.preventDefault();
const value : unknown = entry.retrieve(element); const value : boolean = dom_input.checked;
entry.write(value); this.vertical = value;
await this.update_table(); await this.update_table();
await this.update_entries(); await this.update_entries();
} }
); );
dom_input.checked = this.vertical;
target_element.querySelector(".weekview-controls").appendChild(dom_control);
} }
);
} this.container = target_element.querySelector(".weekview");
// buttons
{
// year
{
/**
* @todo limit
*/
target_element.querySelector(".weekview-control-year-prev").addEventListener(
"click",
async () => {
this.year -= 1;
await this.update_controls();
await this.update_table();
await this.update_entries();
}
);
/**
* @todo limit
*/
target_element.querySelector(".weekview-control-year-next").addEventListener(
"click",
async () => {
this.year += 1;
await this.update_controls();
await this.update_table();
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_table(); await this.update_table();
await this.update_entries(); await this.update_entries();

View file

@ -2,7 +2,7 @@
{ {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
flex-wrap: wrap; flex-wrap: nowrap;
justify-content: right; justify-content: right;
margin-bottom: 12px; margin-bottom: 12px;
@ -10,14 +10,13 @@
text-align: center; text-align: center;
} }
.weekview-control .weekview-controls > *
{ {
flex-basis: 0; flex-basis: 0;
flex-grow: 0; flex-grow: 0;
flex-shrink: 1; flex-shrink: 1;
min-width: 140px; margin: 0 0 0 16px;
margin: 0 12px;
} }
.weekview-control-label .weekview-control-label
@ -30,16 +29,6 @@
*/ */
} }
.weekview-control-input > *
{
display: inline-block;
}
.weekview-control input
{
max-width: 40px;
}
.weekview-table table .weekview-table table
{ {
width: 100%; width: 100%;
@ -87,7 +76,6 @@
.weekview-day .weekview-day
{ {
font-size: 0.75em; font-size: 0.75em;
cursor: help;
} }
.weekview-events .weekview-events

View file

@ -0,0 +1,6 @@
<div class="weekview-control weekview-control-vertical">
<label class="weekview-control-label">{{label}}</label>
<div class="weekview-control-input">
<input type="checkbox"/>
</div>
</div>

View file

@ -1,35 +1,5 @@
<div class="weekview"> <div class="weekview">
<div class="weekview-controls"> <div class="weekview-controls">
<div class="weekview-control weekview-control-year">
<label class="weekview-control-label">{{label_control_year}}</label>
<div class="weekview-control-input">
<button class="weekview-control-year-prev">&#x25C2;</button>
<input type="text"/>
<button class="weekview-control-year-next">&#x25B8;</button>
</div>
</div>
<div class="weekview-control weekview-control-week">
<label class="weekview-control-label">{{label_control_week}}</label>
<div class="weekview-control-input">
<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>

View file

@ -1,3 +1,3 @@
<li class="weekview-event_entry{{additional_classes}}" style="background-color: {{color}};" title="{{title}}" rel="{{rel}}"> <li class="weekview-event_entry{{additional_classes}}" style="background-color: {{color}};" rel="{{rel}}">
{{name}} {{name}}
</li> </li>

View file

@ -1,5 +1,5 @@
<td class="weekview-cell weekview-cell-regular{{extra_classes}}" rel="{{rel}}"> <td class="weekview-cell weekview-cell-regular{{extra_classes}}" rel="{{rel}}">
<span class="weekview-day" title="{{title}}"> <span class="weekview-day">
{{day}} {{day}}
</span> </span>
<ul class="weekview-events"> <ul class="weekview-events">

View file

@ -30,20 +30,38 @@ ${dir_build}/index.html: \
.PHONY: templates .PHONY: templates
templates: \ templates: \
templates-widgets-login \ templates-widgets-special_number_input \
templates-widgets-login_oidc \
templates-widgets-menu \
templates-widgets-sources \ templates-widgets-sources \
templates-widgets-calendar_edit \
templates-widgets-event_edit \
templates-widgets-caldav \
templates-widgets-listview \ templates-widgets-listview \
templates-widgets-weekview \ templates-widgets-weekview \
templates-widgets-mode_switcher \ templates-widgets-overview \
templates-pages-caldav \ templates-widgets-mode_switcher
templates-pages-overview
.PHONY: templates-widgets-login .PHONY: templates-widgets-special_number_input
templates-widgets-login: \ templates-widgets-special_number_input: \
$(wildcard ${dir_source}/widgets/login/templates/*) $(wildcard ${dir_source}/widgets/special_number_input/templates/*)
@ ${cmd_log} "templates:widget:login …" @ ${cmd_log} "templates:widget:special_number_input …"
@ ${cmd_mkdir} ${dir_build}/templates/widget-login @ ${cmd_mkdir} ${dir_build}/templates/widget-special_number_input
@ ${cmd_cp} -r -u -v ${dir_source}/widgets/login/templates/* ${dir_build}/templates/widget-login/ @ ${cmd_cp} -r -u -v ${dir_source}/widgets/special_number_input/templates/* ${dir_build}/templates/widget-special_number_input/
.PHONY: templates-widgets-login_oidc
templates-widgets-login_oidc: \
$(wildcard ${dir_source}/widgets/login_oidc/templates/*)
@ ${cmd_log} "templates:widget:login_oidc …"
@ ${cmd_mkdir} ${dir_build}/templates/widget-login_oidc
@ ${cmd_cp} -r -u -v ${dir_source}/widgets/login_oidc/templates/* ${dir_build}/templates/widget-login_oidc/
.PHONY: templates-widgets-menu
templates-widgets-menu: \
$(wildcard ${dir_source}/widgets/menu/templates/*)
@ ${cmd_log} "templates:widget:menu …"
@ ${cmd_mkdir} ${dir_build}/templates/widget-menu
@ ${cmd_cp} -r -u -v ${dir_source}/widgets/menu/templates/* ${dir_build}/templates/widget-menu/
.PHONY: templates-widgets-sources .PHONY: templates-widgets-sources
templates-widgets-sources: \ templates-widgets-sources: \
@ -52,6 +70,27 @@ templates-widgets-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-calendar_edit
templates-widgets-calendar_edit: \
$(wildcard ${dir_source}/widgets/calendar_edit/templates/*)
@ ${cmd_log} "templates:widget: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}/widgets/event_edit/templates/*)
@ ${cmd_log} "templates:widget:event_edit …"
@ ${cmd_mkdir} ${dir_build}/templates/widget-event_edit
@ ${cmd_cp} -r -u -v ${dir_source}/widgets/event_edit/templates/* ${dir_build}/templates/widget-event_edit/
.PHONY: templates-widgets-caldav
templates-widgets-caldav: \
$(wildcard ${dir_source}/widgets/caldav/templates/*)
@ ${cmd_log} "templates:widget:caldav …"
@ ${cmd_mkdir} ${dir_build}/templates/widget-caldav
@ ${cmd_cp} -r -u -v ${dir_source}/widgets/caldav/templates/* ${dir_build}/templates/widget-caldav/
.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/*)
@ -66,6 +105,13 @@ templates-widgets-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-overview
templates-widgets-overview: \
$(wildcard ${dir_source}/widgets/overview/templates/*)
@ ${cmd_log} "templates:widget:overview …"
@ ${cmd_mkdir} ${dir_build}/templates/widget-overview
@ ${cmd_cp} -r -u -v ${dir_source}/widgets/overview/templates/* ${dir_build}/templates/widget-overview/
.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/*)
@ -73,27 +119,12 @@ templates-widgets-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-pages-caldav
templates-pages-caldav: \
$(wildcard ${dir_source}/pages/caldav/templates/*)
@ ${cmd_log} "templates:page:caldav …"
@ ${cmd_mkdir} ${dir_build}/templates/caldav
@ ${cmd_cp} -r -u -v ${dir_source}/pages/caldav/templates/* ${dir_build}/templates/caldav/
.PHONY: templates-pages-overview
templates-pages-overview: \
$(wildcard ${dir_source}/pages/overview/templates/*)
@ ${cmd_log} "templates:page:overview …"
@ ${cmd_mkdir} ${dir_build}/templates/overview
@ ${cmd_cp} -r -u -v ${dir_source}/pages/overview/templates/* ${dir_build}/templates/overview/
.PHONY: style .PHONY: style
style: ${dir_build}/style.css style: ${dir_build}/style.css
${dir_build}/style.css: \ ${dir_build}/style.css: \
$(wildcard ${dir_source}/style/*.css) \ $(wildcard ${dir_source}/style/*.css) \
$(wildcard ${dir_source}/widgets/*/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} $^ > $@ @ ${cmd_cat} $^ > $@
@ -108,10 +139,9 @@ ${dir_temp}/logic-unlinked.js: \
${dir_source}/base.ts \ ${dir_source}/base.ts \
${dir_source}/types.ts \ ${dir_source}/types.ts \
${dir_source}/model.ts \ ${dir_source}/model.ts \
${dir_source}/helpers.ts \
${dir_source}/overlay.ts \ ${dir_source}/overlay.ts \
${dir_source}/helpers.ts \
$(wildcard ${dir_source}/widgets/*/logic.ts) \ $(wildcard ${dir_source}/widgets/*/logic.ts) \
$(wildcard ${dir_source}/pages/*/logic.ts) \
${dir_source}/main.ts ${dir_source}/main.ts
@ ${cmd_log} "logic | compile …" @ ${cmd_log} "logic | compile …"
@ ${cmd_mkdir} $(dir $@) @ ${cmd_mkdir} $(dir $@)