[task-419] [int]

This commit is contained in:
fenris 2025-10-27 17:22:13 +01:00
parent 63504f4e70
commit bb33ff66b3
12 changed files with 739 additions and 145 deletions

View file

@ -4194,6 +4194,49 @@ declare namespace lib_plankton.zoo_widget {
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 {
/**
*/

View file

@ -12754,6 +12754,230 @@ var lib_plankton;
})(zoo_widget = lib_plankton.zoo_widget || (lib_plankton.zoo_widget = {}));
})(lib_plankton || (lib_plankton = {}));
/*
This file is part of »bacterio-plankton:zoo-widget«.
Copyright 2016-2025 'kcf' <fenris@folksprak.org>
»bacterio-plankton:zoo-widget« 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.
»bacterio-plankton:zoo-widget« 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 »bacterio-plankton:zoo-widget«. If not, see <http://www.gnu.org/licenses/>.
*/
var lib_plankton;
(function (lib_plankton) {
var zoo_widget;
(function (zoo_widget) {
/**
*/
class class_slider {
/**
*/
constructor(content, { "threshold": threshold = 200, "acceleration_function": acceleration_function = (x => (x * 3)), "initial_index": initial_index = 1, } = {}) {
this.conf = {
"content": content,
"threshold": threshold,
"acceleration_function": acceleration_function,
};
this.state = {
"dom_context": null,
"index": initial_index,
"touch_start_position": null,
};
}
/**
*/
static cap(minimum, maximum, value) {
let result = value;
if (minimum !== null) {
result = Math.max(minimum, result);
}
if (maximum !== null) {
result = Math.min(maximum, result);
}
return result;
}
/**
*/
static position_subtract(position1, position2) {
return {
"x": (position1.x - position2.x),
"y": (position1.y - position2.y),
};
}
/**
*/
static touch_event_position(touch_event) {
return ((touch_event.changedTouches.length <= 0)
?
null
:
{
"x": touch_event.changedTouches[0].pageX,
"y": touch_event.changedTouches[0].pageY,
});
}
/**
*/
set_translation(position) {
const difference = (((this.state.touch_start_position === null) || (position === null))
?
null
:
class_slider.position_subtract(position, this.state.touch_start_position));
this.state.dom_context.querySelector(".slides").style.transform = (([
lib_plankton.string.coin("translateX({{value}}%)", {
"value": ((this.state.index - 1) * (-100)).toFixed(0)
})
]
.concat((difference === null)
?
[]
:
[
//
lib_plankton.string.coin("translateX({{value}}px)", {
"value": class_slider.cap(((this.state.index >= this.conf.content.length)
?
(-this.conf.threshold)
:
null), ((this.state.index <= 1)
?
this.conf.threshold
:
null), this.conf.acceleration_function(difference.x)).toFixed(0),
})
]))
.join(" "));
}
/**
*/
start(position) {
if (position === null) {
// do nothing
}
else {
if (this.state.touch_start_position === null) {
this.state.touch_start_position = position;
}
else {
// do nothing
}
}
}
/**
*/
update(position) {
if (position === null) {
// do nothing
}
else {
if (this.state.touch_start_position === null) {
// do nothing
}
else {
this.set_translation(position);
}
}
}
/**
*/
finish(position) {
if (position === null) {
this.state.touch_start_position = null;
// do nothing
}
else {
if (this.state.touch_start_position === null) {
// do nothing
}
else {
const difference = class_slider.position_subtract(position, this.state.touch_start_position);
const offset = this.conf.acceleration_function(difference.x);
const index_increment = (() => {
if (offset <= -this.conf.threshold) {
return ((this.state.index >= this.conf.content.length) ? 0 : (+1));
}
else if ((offset > -this.conf.threshold) && (offset < this.conf.threshold)) {
return 0;
}
else if (offset >= this.conf.threshold) {
return ((this.state.index <= 1) ? 0 : (-1));
}
else {
// not possible
}
})();
this.state.index += index_increment;
this.set_translation(null);
this.state.touch_start_position = null;
}
}
}
/**
*/
async load(target_element) {
this.state.dom_context = target_element;
// structure
{
const dom_container = document.createElement("div");
{
dom_container.style.overflow = "hidden";
dom_container.style.whiteSpace = "nowrap";
}
{
const dom_content = document.createElement("div");
{
dom_content.style.height = "100%";
dom_content.style.width = "100%";
dom_content.style.transition = "transform 0.25s ease-in-out";
}
dom_content.classList.add("slides");
for (const slide of this.conf.content) {
const dom_slide = document.createElement("div");
dom_slide.classList.add("slide");
{
dom_slide.style.display = "inline-block";
dom_slide.style.height = "100%";
dom_slide.style.width = "100%";
dom_slide.style.transition = "width 0.25s ease-in-out";
}
await slide.load(dom_slide);
dom_content.appendChild(dom_slide);
}
dom_container.appendChild(dom_content);
}
this.state.dom_context.appendChild(dom_container);
}
// listeners
{
this.state.dom_context.addEventListener("touchstart", (event) => {
this.start(class_slider.touch_event_position(event));
});
this.state.dom_context.addEventListener("touchmove", (event) => {
this.update(class_slider.touch_event_position(event));
});
this.state.dom_context.addEventListener("touchend", (event) => {
this.finish(class_slider.touch_event_position(event));
});
this.state.dom_context.addEventListener("touchcancel", (event) => {
this.finish(null);
});
}
this.set_translation(null);
}
}
zoo_widget.class_slider = class_slider;
})(zoo_widget = lib_plankton.zoo_widget || (lib_plankton.zoo_widget = {}));
})(lib_plankton || (lib_plankton = {}));
/*
This file is part of »bacterio-plankton:zoo-page«.
Copyright 2016-2025 'kcf' <fenris@folksprak.org>

View file

@ -23,6 +23,23 @@ along with »dali«. If not, see <http://www.gnu.org/licenses/>.
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> = {};
@ -32,12 +49,13 @@ namespace _dali.helpers
*/
export function view_mode_determine(
mode_descriptor : string
) : _dali.enum_view_mode
)
: _dali.enum_view_mode
{
if (mode_descriptor === "auto")
{
return (
(window.innerWidth >= 800)
(window.innerWidth >= 1000)
?
_dali.enum_view_mode.week
:
@ -51,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(

View file

@ -28,33 +28,9 @@ namespace _dali.pages.overview
async (parameters, target_element) => {
// params
const view_mode : _dali.enum_view_mode = _dali.helpers.view_mode_determine(parameters["mode"] ?? "auto");
const view_kind : _dali.enum_view_kind = _dali.helpers.view_kind_determine(parameters["kind"] ?? "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(
@ -125,7 +101,7 @@ namespace _dali.pages.overview
: Promise<void>
{
await widget_sources.update({"priviliged": priviliged});
await widget_weekview.update_entries();
await widget_multiview.update_entries();
}
/**
@ -146,7 +122,7 @@ namespace _dali.pages.overview
)
: Promise<void>
{
await widget_weekview.update_entries();
await widget_multiview.update_entries();
}
/**
@ -501,6 +477,70 @@ namespace _dali.pages.overview
await widget.load(_dali.overlay.get_content_element());
}
const widget_sources : _dali.widgets.sources.class_widget_sources = (
new _dali.widgets.sources.class_widget_sources(
_dali.model.calendar_list,
{
"initial_priviliged": _dali.is_logged_in(),
"action_add": action_create_calendar,
"action_select": (entry) => action_edit_calendar(entry),
"action_toggle": (entry, mode) => {
widget_multiview.toggle_calendar_visibilty(entry.id, {"mode": mode});
},
}
)
);
const widget_multiview : _dali.widgets.multiview.class_widget_multiview = (
new _dali.widgets.multiview.class_widget_multiview(
get_entries,
{
"initial_view_mode": view_mode,
"action_create_event": action_create_event,
"action_edit_event": action_edit_event,
}
)
);
target_element.innerHTML = await _dali.helpers.template_coin(
"overview",
"main",
{
}
);
switch (view_kind)
{
case _dali.enum_view_kind.regular:
{
target_element.querySelector("#overview-body").innerHTML = await _dali.helpers.template_coin(
"overview",
"body-regular",
{
}
);
await widget_sources.load(target_element.querySelector("#overview-pane-left"));
await widget_multiview.load(target_element.querySelector("#overview-pane-right"));
break;
}
case _dali.enum_view_kind.touch:
{
const widget_slider = new lib_plankton.zoo_widget.class_slider(
[
widget_sources,
widget_multiview,
],
{
"threshold": 100,
"initial_index": 2,
}
)
await widget_slider.load(target_element.querySelector("#overview-body"));
break;
}
}
// hint
{
const dom_hint = target_element.querySelector("#overview-hint");
@ -510,79 +550,8 @@ namespace _dali.pages.overview
// mode switcher
{
widget_mode_switcher = new _dali.widgets.mode_switcher.class_widget_mode_switcher(
[
{
"mode": _dali.enum_view_mode.week,
"label": lib_plankton.translate.get("page.overview.mode.week"),
},
{
"mode": _dali.enum_view_mode.list,
"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);
}
}
);
set_view_mode(view_mode);
await widget_mode_switcher.load(target_element.querySelector("#overview-mode"));
}
// sources
{
widget_sources = new _dali.widgets.sources.class_widget_sources(
_dali.model.calendar_list,
{
"initial_priviliged": _dali.is_logged_in(),
"action_add": action_create_calendar,
"action_select": (entry) => action_edit_calendar(entry),
"action_toggle": (entry, mode) => {
widget_weekview.toggle_visibility(entry.id, {"mode": mode});
widget_listview.toggle_visibility(entry.id, {"mode": mode});
},
}
);
await widget_sources.load(target_element.querySelector("#overview-pane-left"));
}
// weekview
{
widget_weekview = (
new _dali.widgets.weekview.class_widget_weekview(
get_entries,
{
"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
{
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"));
// set_view_mode(view_mode);
// await widget_mode_switcher.load(target_element.querySelector("#overview-mode"));
}
_dali.model.listen_reset(

View file

@ -34,13 +34,3 @@
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

@ -0,0 +1,4 @@
<div id="overview-pane-left">
</div>
<div id="overview-pane-right">
</div>

View file

@ -0,0 +1 @@

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

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

View file

@ -25,7 +25,8 @@ namespace _dali
/**
*/
export enum enum_access_level {
export enum enum_access_level
{
none,
view,
edit,
@ -202,20 +203,12 @@ namespace _dali
};
/**
*/
export enum enum_view_mode
{
week,
list,
}
/**
*/
export function access_level_encode(
access_level : _dali.enum_access_level
) : ("none" | "view" | "edit" | "admin")
)
: ("none" | "view" | "edit" | "admin")
{
switch (access_level)
{
@ -231,7 +224,8 @@ namespace _dali
*/
export function access_level_decode(
representation : /*("none" | "view" | "edit" | "admin")*/string
) : _dali.enum_access_level
)
: _dali.enum_access_level
{
switch (representation)
{
@ -244,11 +238,21 @@ namespace _dali
}
/**
*/
export enum enum_view_mode
{
week,
list,
}
/**
*/
export function view_mode_encode(
mode : _dali.enum_view_mode
) : string
)
: string
{
switch (mode)
{
@ -263,7 +267,8 @@ namespace _dali
*/
export function view_mode_decode(
view_mode_encoded : string
) : _dali.enum_view_mode
)
: _dali.enum_view_mode
{
const map : Record<string, _dali.enum_view_mode> = {
"week": _dali.enum_view_mode.week,
@ -279,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,269 @@
/*
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.multiview
{
/**
*/
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 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.mode_switcher.class_widget_mode_switcher;
/**
*/
private widget_weekview : _dali.widgets.weekview.class_widget_weekview;
/**
*/
private widget_listview : _dali.widgets.listview.class_widget_listview;
/**
*/
public constructor(
get_entries : type_get_entries,
{
"initial_view_mode": initial_view_mode = _dali.enum_view_mode.week,
"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;
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.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 update_entries(
)
:
Promise<void>
{
return this.widget_weekview.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.mode_switcher.class_widget_mode_switcher(
[
{
"mode": _dali.enum_view_mode.week,
"label": lib_plankton.translate.get("page.overview.mode.week"),
},
{
"mode": _dali.enum_view_mode.list,
"label": lib_plankton.translate.get("page.overview.mode.list"),
},
],
{
"initial_selection": this.initial_view_mode,
"action_change": (view_mode) => {
console.warn("todo");
/*
lib_plankton.zoo_page.set(
{
"name": "overview",
"parameters": {
"mode": _dali.view_mode_encode(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.weekview.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}),
}
)
);
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.listview.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 {}