aboutsummaryrefslogtreecommitdiff
path: root/static/ts/components
diff options
context:
space:
mode:
authorHugo Hörnquist <hugo@lysator.liu.se>2023-09-13 00:01:28 +0200
committerHugo Hörnquist <hugo@lysator.liu.se>2023-09-13 00:01:28 +0200
commita82b6c772089aa46e30c6c89ef48f514294df3cb (patch)
treee25d9b6fd1fefe8b6ac293a5c0b53293872a8f54 /static/ts/components
parentAdd basic documentation for lens. (diff)
parentEven more documentation. (diff)
downloadcalp-a82b6c772089aa46e30c6c89ef48f514294df3cb.tar.gz
calp-a82b6c772089aa46e30c6c89ef48f514294df3cb.tar.xz
Merge branch 'next' into datarewrite-structures
Diffstat (limited to '')
-rw-r--r--static/ts/components.ts (renamed from static/components.ts)22
-rw-r--r--static/ts/components/changelog.ts (renamed from static/components/changelog.ts)30
-rw-r--r--static/ts/components/date-jump.ts51
-rw-r--r--static/ts/components/date-time-input.ts (renamed from static/components/date-time-input.ts)70
-rw-r--r--static/ts/components/edit-rrule.ts (renamed from static/components/edit-rrule.ts)12
-rw-r--r--static/ts/components/input-list.ts (renamed from static/components/input-list.ts)84
-rw-r--r--static/ts/components/popup-element.ts (renamed from static/components/popup-element.ts)46
-rw-r--r--static/ts/components/slider.ts (renamed from static/components/slider.ts)70
-rw-r--r--static/ts/components/tab-group-element.ts (renamed from static/components/tab-group-element.ts)58
-rw-r--r--static/ts/components/vevent-block.ts (renamed from static/components/vevent-block.ts)23
-rw-r--r--static/ts/components/vevent-description.ts (renamed from static/components/vevent-description.ts)16
-rw-r--r--static/ts/components/vevent-dl.ts (renamed from static/components/vevent-dl.ts)12
-rw-r--r--static/ts/components/vevent-edit.ts (renamed from static/components/vevent-edit.ts)11
-rw-r--r--static/ts/components/vevent.ts (renamed from static/components/vevent.ts)44
14 files changed, 497 insertions, 52 deletions
diff --git a/static/components.ts b/static/ts/components.ts
index e5fabba6..c78b5753 100644
--- a/static/components.ts
+++ b/static/ts/components.ts
@@ -1,3 +1,12 @@
+/**
+ Actuall creation of all web components.
+
+ More text
+
+ @category Web Components
+ @module components
+ */
+
import { ComponentDescription } from './components/vevent-description'
import { ComponentEdit } from './components/vevent-edit'
import { VEventDL } from './components/vevent-dl'
@@ -13,9 +22,20 @@ import { DateJump } from './components/date-jump'
export { initialize_components }
-function initialize_components() {
+/**
+ Create web components from all our components.
+
+ The reason each components module doesn't simply initialize its own component
+ is due to some components needing to be initialized AFTER some global
+ variables (see inline comments).
+ @TODO
+ Fix the initialization order dependency
+ @TODO
+ or otherwise have a static field on each component specifying it's desired name.
+ */
+function initialize_components() {
/* These MUST be created AFTER vcal_objcets and event_calendar_mapping are
inistialized, since their constructors assume that that piece of global
state is available */
diff --git a/static/components/changelog.ts b/static/ts/components/changelog.ts
index d08f7cb3..8f8adc1c 100644
--- a/static/components/changelog.ts
+++ b/static/ts/components/changelog.ts
@@ -1,21 +1,43 @@
+/**
+ `<changelog />`
+
+ Display of a VEvents changelog. @ref{ChangeLogEntry}
+
+ TODO rename this file!
+
+
+ @privateRemarks @anchor{VEventChangelog}
+
+ @category Web Components
+ @mergeTarget components
+ @module
+*/
import { makeElement } from '../lib'
import { ComponentVEvent } from './vevent'
import { VEvent } from '../vevent'
export { VEventChangelog }
+/**
+ Component displaying veevents changelog.
+
+ This component is dumb, and (almost) doesn't keep any internal state. Instead
+ other parts of the program should call it with a `VEvent`, which contains the
+ actual changelog.
+*/
class VEventChangelog extends ComponentVEvent {
- readonly ul: HTMLElement
+ /** The list holding the changelog */
+ readonly #ul: HTMLElement
constructor(uid?: string) {
super(uid);
- this.ul = makeElement('ul');
+ this.#ul = makeElement('ul');
}
connectedCallback() {
- this.replaceChildren(this.ul);
+ this.replaceChildren(this.#ul);
}
redraw(data: VEvent) {
@@ -44,6 +66,6 @@ class VEventChangelog extends ComponentVEvent {
children.push(makeElement('li', { textContent: msg }));
}
- this.ul.replaceChildren(...children)
+ this.#ul.replaceChildren(...children)
}
}
diff --git a/static/ts/components/date-jump.ts b/static/ts/components/date-jump.ts
new file mode 100644
index 00000000..f1cfe7e6
--- /dev/null
+++ b/static/ts/components/date-jump.ts
@@ -0,0 +1,51 @@
+/**
+ `<date-jump />`
+
+ @category Web Components
+ @mergeTarget components
+ @module
+*/
+
+export { DateJump }
+
+/** Replace backend-driven [today] link with frontend, with one that
+ gets correctly set in the frontend. Similarly, update the go to
+ specific date button into a link which updates wheneven the date
+ form updates.
+
+ TODO is this comment correct? We somehow contain an input element also.
+*/
+class DateJump extends HTMLElement {
+
+ readonly #golink: HTMLAnchorElement;
+ readonly #input: HTMLInputElement;
+
+ constructor() {
+ super();
+
+ this.#golink = document.createElement('a')
+ this.#golink.classList.add('btn');
+ this.#golink.textContent = "➔"
+ this.#input = document.createElement('input')
+ this.#input.type = 'date';
+ }
+
+ /** Sets the link to NOW upon mounting */
+ connectedCallback() {
+
+ /* Form is just here so the css works out */
+ let form = document.createElement('form');
+ form.replaceChildren(this.#input, this.#golink);
+ this.replaceChildren(form);
+
+ this.#input.onchange = () => {
+ let date = this.#input.valueAsDate!.format('~Y-~m-~d');
+ this.#golink.href = `${date}.html`
+ }
+
+ let now = (new Date).format("~Y-~m-~d")
+ this.#input.value = now;
+ /* onchange isn't triggered by manually setting the value */
+ this.#golink.href = `${now}.html`
+ }
+}
diff --git a/static/components/date-time-input.ts b/static/ts/components/date-time-input.ts
index 20e9a505..33201653 100644
--- a/static/components/date-time-input.ts
+++ b/static/ts/components/date-time-input.ts
@@ -1,12 +1,35 @@
+/**
+ * `<date-time-input />`
+ *
+ * @category Web Components
+ * @mergeTarget components
+ * @module
+ */
+
export { DateTimeInput }
import { makeElement, parseDate } from '../lib'
-
-/* '<date-time-input />' */
+/**
+ * The HTML component `<date-time-input />`.
+ * An element for input for date-times. Similar to
+ * @example
+ * ```html
+ * <input type="date"/>
+ * <input type="time"/>
+ * ```
+ *
+ * But as a single unit.
+ *
+ * ### Attributes
+ * - dateonly
+ *
+ */
class DateTimeInput extends /* HTMLInputElement */ HTMLElement {
+ /** Our time input element */
readonly time: HTMLInputElement;
+ /** Our date input element */
readonly date: HTMLInputElement;
constructor() {
@@ -22,22 +45,30 @@ class DateTimeInput extends /* HTMLInputElement */ HTMLElement {
}) as HTMLInputElement
}
+ /**
+ We set our children first when mounted.
+
+ This can be in the constructor for chromium, but NOT firefox...
+
+ - Vivaldi 4.3.2439.63 stable
+ - Mozilla Firefox 94.0.1
+ */
connectedCallback() {
- /* This can be in the constructor for chromium, but NOT firefox...
- Vivaldi 4.3.2439.63 stable
- Mozilla Firefox 94.0.1
- */
- /*
- https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes#boolean_attributes
- https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute
- */
this.replaceChildren(this.date, this.time)
}
+ /**
+ Attributes which we want notifications when they are change.
+
+ Part of the Web Component API
+
+ - `dateonly`
+ */
static get observedAttributes() {
return ['dateonly']
}
+ /** Part of the Web Component API */
attributeChangedCallback(name: string, _: string | null, to: string | null): void {
switch (name) {
case 'dateonly':
@@ -54,10 +85,16 @@ class DateTimeInput extends /* HTMLInputElement */ HTMLElement {
}
}
+ /**
+ Setting this to true disabled the time part of the input, and makes
+ any output only have date components (alternativly, the time component
+ set to zero).
+ */
get dateonly(): boolean {
return this.hasAttribute('dateonly');
}
+ /** See getter */
set dateonly(b: boolean) {
if (b) {
this.setAttribute('dateonly', "");
@@ -66,6 +103,7 @@ class DateTimeInput extends /* HTMLInputElement */ HTMLElement {
}
}
+ /** See getter */
set value(date: Date) {
let [d, t] = date.format("~L~Y-~m-~dT~H:~M").split('T');
this.date.value = d;
@@ -74,6 +112,7 @@ class DateTimeInput extends /* HTMLInputElement */ HTMLElement {
this.dateonly = date.dateonly;
}
+ /** Returns current value as a Date object. */
get value(): Date {
let dt;
let date = this.date.value;
@@ -88,6 +127,7 @@ class DateTimeInput extends /* HTMLInputElement */ HTMLElement {
return dt;
}
+ /** Returns current value as an ISO-8601 formatted string. */
get stringValue(): string {
if (this.dateonly) {
return this.value.format("~Y-~m-~d")
@@ -96,6 +136,13 @@ class DateTimeInput extends /* HTMLInputElement */ HTMLElement {
}
}
+ /**
+ Set the selected date.
+
+ @param new_value
+ If given a date, set the input to that date.
+ If given a string, parse it as an ISO-8601 formatted datetime.
+ */
set stringValue(new_value: Date | string) {
let date, time, dateonly = false;
if (new_value instanceof Date) {
@@ -110,6 +157,9 @@ class DateTimeInput extends /* HTMLInputElement */ HTMLElement {
this.time.value = time;
}
+ /**
+ Adds an event listener to both the date and time input.
+ */
addEventListener(type: string, proc: ((e: Event) => void)) {
if (type != 'input') throw "Only input supported";
diff --git a/static/components/edit-rrule.ts b/static/ts/components/edit-rrule.ts
index a361bdee..b78171cc 100644
--- a/static/components/edit-rrule.ts
+++ b/static/ts/components/edit-rrule.ts
@@ -1,3 +1,15 @@
+/**
+ * `<vevent-edit-rrule />`
+ *
+ * An edit form for a recurrence rule. Searches its template for elements
+ * with `[name="<<field name>>"]`, and binds to those.
+ *
+ * TODO rename this file
+ *
+ * @category Web Components
+ * @mergeTarget components
+ * @module
+ */
export { EditRRule }
import { ComponentVEvent } from './vevent'
diff --git a/static/components/input-list.ts b/static/ts/components/input-list.ts
index 0afd4999..72d27cab 100644
--- a/static/components/input-list.ts
+++ b/static/ts/components/input-list.ts
@@ -1,29 +1,75 @@
+/**
+ * `<input-list />`
+ *
+ * A list of identical input fields, which forms a group. For example
+ * useful to handle keywords.
+ *
+ * @category Web Components
+ * @mergeTarget components
+ * @module
+ */
export { InputList }
/*
TODO allow each item to be a larger unit, possibly containing multiple input
fields.
*/
+/**
+ A multi-valued input, done by creating extra input fields as needed.
+
+ The first element of body MUST be an input element, which will be used as the
+ template for each instance. A tag input could for example look like
+
+ @example
+ ```html
+ <input-list name="tags">
+ <input type="text" placeholder="tag ..." />
+ </input-list>
+ ```
+
+ Whenever one of the input elements `value` becomes the empty string, that tag
+ is removed, and whenever there is no element with the empty string as a
+ `value`, a new input element will be added onto the end.
+ */
class InputList extends HTMLElement {
- el: HTMLInputElement;
+ /** The element used as our template. Will be sourced from the initial HTML code. */
+ #el: HTMLInputElement;
+ /**
+ Registered listeners, which will be added onto each created entry
+
+ Keys are event names ('input', 'change', ...) and values event handlers.
+
+ This is a list of tuples rather than a dictionary, since multiple
+ listeners of the same type can be registered.
+ */
#listeners: [string, (e: Event) => void][] = [];
constructor() {
super();
- this.el = this.children[0].cloneNode(true) as HTMLInputElement;
+ this.#el = this.children[0].cloneNode(true) as HTMLInputElement;
}
+ /** Clears all existing children upon mount */
connectedCallback() {
for (let child of this.children) {
child.remove();
}
- this.addInstance();
+ this.#addInstance();
}
- createInstance(): HTMLInputElement {
- let new_el = this.el.cloneNode(true) as HTMLInputElement
+ /**
+ Instanciates a new instance of the input element.
+
+ An event listener for 'input' will be added, which will handle the
+ addition and removing of other elements.
+
+ All event listeners attachet on the input-list component will also be
+ added.
+ */
+ #createInstance(): HTMLInputElement {
+ let new_el = this.#el.cloneNode(true) as HTMLInputElement
let that = this;
new_el.addEventListener('input', function() {
/* TODO .value is empty both if it's actually empty, but also
@@ -39,7 +85,7 @@ class InputList extends HTMLElement {
}
} else {
if (!this.nextElementSibling) {
- that.addInstance();
+ that.#addInstance();
// window.setTimeout(() => this.focus())
this.focus();
}
@@ -53,12 +99,17 @@ class InputList extends HTMLElement {
return new_el;
}
- addInstance() {
- let new_el = this.createInstance();
+ /** Add a new instance of the input element to the container */
+ #addInstance() {
+ let new_el = this.#createInstance();
this.appendChild(new_el);
}
- get value(): any[] {
+ /**
+ * The value from each element, except the last which should always be empty.
+ * Has an unspecified type, since children:s value field might give non-strings.
+ */
+ get value(): unknown[] {
let value_list = []
for (let child of this.children) {
value_list.push((child as any).value);
@@ -69,6 +120,12 @@ class InputList extends HTMLElement {
return value_list
}
+ /**
+ Overwrite the current value with a new one.
+
+ Each entry in the array will be mapped unto one instance of the template
+ input element. A final empty element will also be added.
+ */
set value(new_value: any[]) {
let all_equal = true;
@@ -97,17 +154,22 @@ class InputList extends HTMLElement {
/* clear dictionary */
values.set(value, false);
} else {
- let new_el = this.createInstance();
+ let new_el = this.#createInstance();
new_el.value = value;
output_list.push(new_el);
}
}
/* final, trailing, element */
- output_list.push(this.createInstance());
+ output_list.push(this.#createInstance());
this.replaceChildren(...output_list);
}
+ /**
+ Add an event listener to each of the inputs.
+
+ This basically works as the "regular" version.
+ */
addEventListener(type: string, proc: ((e: Event) => void)) {
// if (type != 'input') throw "Only input supported";
diff --git a/static/components/popup-element.ts b/static/ts/components/popup-element.ts
index 458f543c..a1e81f0e 100644
--- a/static/components/popup-element.ts
+++ b/static/ts/components/popup-element.ts
@@ -1,3 +1,19 @@
+/**
+ * `<popup-element />`
+ *
+ * A (small) floating window containing information, which can be dragged
+ * arround. Consists of a navigation bar with a few buttons for
+ * controlling the window, which also works as a drag handle, along with
+ * an area for contents, which can be resized by the user.
+
+ * Currently tightly coupled to VEvent's, since their color
+ * profile is derived from their owning events calendar, and they have
+ * action buttons for the event in their navigation bar.
+ *
+ * @category Web Components
+ * @mergeTarget components
+ * @module
+ */
export { PopupElement, setup_popup_element }
import { VEvent } from '../vevent'
@@ -7,12 +23,19 @@ import { ComponentVEvent } from './vevent'
import { remove_event } from '../server_connect'
-/* <popup-element /> */
+/**
+ ### Attributes
+ - visible
+ */
class PopupElement extends ComponentVEvent {
/* The popup which is the "selected" popup.
- /* Makes the popup last hovered over the selected popup, moving it to
+ * Makes the popup last hovered over the selected popup, moving it to
* the top, and allowing global keyboard bindings to affect it. */
+ /**
+ The popup which was most recently interacted with by the user. Used to
+ move it on top of all others, as well as sending relevant key events there.
+ */
static activePopup: PopupElement | null = null;
constructor(uid?: string) {
@@ -67,6 +90,7 @@ class PopupElement extends ComponentVEvent {
this.replaceChildren(body);
}
+ /** ['visible'] */
static get observedAttributes() {
return ['visible'];
}
@@ -76,15 +100,22 @@ class PopupElement extends ComponentVEvent {
case 'visible':
if (newValue !== null)
/* Only run resize code when showing the popup */
- this.onVisibilityChange()
+ this.#onVisibilityChange()
break;
}
}
+ /**
+ If the popup is currently visible.
+
+ Adds the `visible` attribute to the component, which must then be handled
+ through CSS.
+ */
get visible(): boolean {
return this.hasAttribute('visible');
}
+ /** Set the visibility status of the component. */
set visible(isVisible: boolean) {
if (isVisible) {
this.setAttribute('visible', 'visible');
@@ -93,7 +124,7 @@ class PopupElement extends ComponentVEvent {
}
}
- private onVisibilityChange() {
+ #onVisibilityChange() {
console.log('here');
/* TODO better way to find root */
@@ -128,6 +159,10 @@ class PopupElement extends ComponentVEvent {
el.style.removeProperty('height');
}
+ /**
+ Resize the popup window to fill the current viewport (mostly). Is
+ probably bonud to the maximize button in the navigation bar.
+ */
maximize() {
/* TODO this assumes that popups are direct decendant of their parent,
which they really ought to be */
@@ -144,7 +179,8 @@ class PopupElement extends ComponentVEvent {
}
}
-/* Create a new popup element for the given VEvent, and ready it for editing the
+/**
+ Create a new popup element for the given VEvent, and ready it for editing the
event. Used when creating event (through the frontend).
The return value can safely be ignored.
*/
diff --git a/static/components/slider.ts b/static/ts/components/slider.ts
index 48abc91b..8be66a73 100644
--- a/static/components/slider.ts
+++ b/static/ts/components/slider.ts
@@ -1,24 +1,58 @@
-export { SliderInput }
+/**
+ <slider-input />
+
+ A Web Component implementing a slider with a corresponding number input.
+
+ TODO rename this file
+
+ ### Parameters
+
+ All of these are optional, see {@linkcode dflt} for defaults.
+
+ #### min
+ Minimum allowed value.
+
+ #### max
+ Maximum allowed value.
+
+ #### step
+ How large each step of the slider/number box should be.
+
+ @module
+*/
+
+export { SliderInput, Attribute, dflt }
import { makeElement } from '../lib'
+/** Defalut values for all attributes, if not given */
const dflt = {
min: 0,
max: 100,
step: 1,
}
+/** Valid attributes for SliderInput */
type Attribute = 'min' | 'max' | 'step'
+/**
+ Component displaying an input slider, together with a corresponding numerical
+ input
+*/
class SliderInput extends HTMLElement {
/* value a string since javascript kind of expects that */
- #value = "0";
- min = 0;
- max = 100;
- step = 1;
-
+ #value = "" + dflt.min
+ /** Minimum allowed value */
+ min = dflt.min
+ /** Maximum allowed value */
+ max = dflt.max
+ /** How large each step should be */
+ step = dflt.step
+
+ /** The HTML slider component */
readonly slider: HTMLInputElement;
+ /** The HTML number input component */
readonly textIn: HTMLInputElement;
constructor(min?: number, max?: number, step?: number, value?: number) {
@@ -48,8 +82,8 @@ class SliderInput extends HTMLElement {
value: this.value,
}) as HTMLInputElement
- this.slider.addEventListener('input', e => this.propagate(e));
- this.textIn.addEventListener('input', e => this.propagate(e));
+ this.slider.addEventListener('input', e => this.#propagate(e));
+ this.textIn.addEventListener('input', e => this.#propagate(e));
/* MUST be after sub components are bound */
this.value = "" + (value || this.getAttribute('value') || defaultValue);
@@ -59,7 +93,7 @@ class SliderInput extends HTMLElement {
this.replaceChildren(this.slider, this.textIn);
}
-
+ /** ['min', 'max', 'step'] */
static get observedAttributes(): Attribute[] {
return ['min', 'max', 'step']
}
@@ -75,19 +109,35 @@ class SliderInput extends HTMLElement {
this[name] = parseFloat(to || "" + dflt[name])
}
- propagate(e: Event) {
+ /**
+ Helper for updating the value attribute
+
+ Event listeners are bound on both the input elements, which both simply
+ call this. This procedure then updates the classes value field.
+
+ TODO `oninput`?
+ */
+ #propagate(e: Event) {
this.value = (e.target as HTMLInputElement).value;
if (e instanceof InputEvent && this.oninput) {
this.oninput(e);
}
}
+ /**
+ Set a new numerical value.
+
+ A number not possible due to the current `min`, `max`, and `step`
+ properties can be set and will work, the slider will however not
+ properly show it, but rather the closest value it can display.
+ */
set value(value: string) {
this.slider.value = value;
this.textIn.value = value;
this.#value = value;
}
+ /** Get the current numerical value */
get value(): string {
return this.#value;
}
diff --git a/static/components/tab-group-element.ts b/static/ts/components/tab-group-element.ts
index e90997e9..bcd45b40 100644
--- a/static/components/tab-group-element.ts
+++ b/static/ts/components/tab-group-element.ts
@@ -1,3 +1,40 @@
+/**
+ * `<tab-group />`
+
+A group of tabs, where only one can be visible at a time.
+
+@privateRemarks TODO which form does the HTML document have? For CSS purposes
+
+Each tab consists of two parts, a label which is used for selecting
+it, and a tab-element, which contains the actual content. These two
+should refer to each other as follows:
+
+@example
+```
++---------------+ +-----------------+
+| TabLabel | | Tab |
++---------------+ +-----------------+
+| id |<----| aria-labelledby |
+| aria-controls |---->| id |
++---------------+ +-----------------+
+```
+
+Further information about tabs in HTML can be found here:
+https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/Tab_Role
+
+#### CSS Variables
+
+##### tabcount
+Each tab element has the style property `--tabcount` set to how
+many tabs it has. This is mostly useful to make sure the tab context
+is large enough to fit all tab labels without overflowing.
+
+ *
+ * @category Web Components
+ * @mergeTarget components
+ * @module
+ */
+
import { ComponentVEvent } from './vevent'
import { makeElement, gensym } from '../lib'
import { EditRRule } from './edit-rrule'
@@ -6,7 +43,7 @@ import { vcal_objects } from '../globals'
export { TabGroupElement }
-/* Lacks a template, since it's trivial
+/** Lacks a template, since it's trivial
The initial children of this element all becomes tabs, each child may have
the datapropertys 'label' and 'title' set, where label is what is shown in
the tab bar, and title is the hower text.
@@ -21,9 +58,12 @@ export { TabGroupElement }
*/
class TabGroupElement extends ComponentVEvent {
+ /** The container holding all the tabLabels */
readonly menu: HTMLElement;
+ /** Contents of each tab */
tabs: HTMLElement[] = [];
+ /** Label element of each tab */
tabLabels: HTMLElement[] = [];
constructor(uid?: string) {
@@ -81,6 +121,12 @@ class TabGroupElement extends ComponentVEvent {
} /* end connectedCallback */
+ /**
+ Adds a new tab to the group. The first parameter will make up the body
+ of the tab. The label is whath should be shown in the tab selector,
+ but defaults to the first letter of the text content of the body node.
+ Title is the hoover text of the label.
+ */
addTab(child: HTMLElement, label?: string, title?: string) {
/* First character of text is a good a guess as any for our label,
@@ -123,11 +169,15 @@ class TabGroupElement extends ComponentVEvent {
this.tabLabels.push(tabLabel);
this.menu.appendChild(tabLabel);
- tabLabel.addEventListener('click', () => this.tabClickedCallback(tabLabel));
+ tabLabel.addEventListener('click', () => this.#tabClickedCallback(tabLabel));
this.style.setProperty('--tabcount', '' + this.tabs.length);
}
+ /**
+ HTMLElement must be one of the tab bodies in this group. This method
+ removes it, along with its TabLabel.
+ */
removeTab(tab: HTMLElement) {
let id = tab.getAttribute('aria-labelledby')!
let label = document.getElementById(id)
@@ -152,7 +202,7 @@ class TabGroupElement extends ComponentVEvent {
}
/* TODO replace querySelectors here with our already saved references */
- tabClickedCallback(tab: Element) {
+ #tabClickedCallback(tab: Element) {
/* hide all tab panels */
for (let tabcontent of this.querySelectorAll('[role="tabpanel"]')) {
@@ -171,7 +221,7 @@ class TabGroupElement extends ComponentVEvent {
}
- /* returns our rrule tab if we have one */
+ /** Return our rrule tab if we have one */
has_rrule_tab(): Element | false {
for (let child of this.children) {
if (child.firstChild! instanceof EditRRule) {
diff --git a/static/components/vevent-block.ts b/static/ts/components/vevent-block.ts
index 9bbb8e7e..90460740 100644
--- a/static/components/vevent-block.ts
+++ b/static/ts/components/vevent-block.ts
@@ -1,3 +1,14 @@
+/**
+ * `<vevent-block />`
+ *
+ * A block in our graphical view.
+ *
+ * Unique in that it works quite differently between the week and month view.
+ *
+ * @category Web Components
+ * @mergeTarget components
+ * @module
+ */
export { ComponentBlock }
import { ComponentVEvent } from './vevent'
@@ -5,10 +16,16 @@ import { VEvent } from '../vevent'
import { parseDate, to_local } from '../lib'
-/* <vevent-block />
+/**
+ A graphical block in the inline view.
- A grahpical block in the week view.
-*/
+ The back-end links what should become these to elements in the sidebar
+ containing extra info, jumping between them using fragment links.
+ That functionality is removed when we replace the non-js fallback children of
+ these elements, but we instead link it to a
+ {@linkcode components/popup-element.PopupElement}
+ containing the detailed information, along with editing controls and more.
+ */
class ComponentBlock extends ComponentVEvent {
constructor(uid?: string) {
super(uid);
diff --git a/static/components/vevent-description.ts b/static/ts/components/vevent-description.ts
index b44185e7..bf62c10d 100644
--- a/static/components/vevent-description.ts
+++ b/static/ts/components/vevent-description.ts
@@ -1,3 +1,19 @@
+/**
+ * `<vevent-description />`
+
+A text representation of a VEvent. Used as the summary tab of our
+popup windows, and in the sidebar.
+
+When redrawn, it looks for an HTML-tag inside its template having the
+attribute `data-property` matching the properties name. If one is
+found, it looks in the `formatters` table
+({@link formatters}), for a field matching the property value, and
+defaults to the key `default`.
+ *
+ * @category Web Components
+ * @mergeTarget components
+ * @module
+ */
export { ComponentDescription }
import { VEvent } from '../vevent'
diff --git a/static/components/vevent-dl.ts b/static/ts/components/vevent-dl.ts
index a792c07f..a4b51dd9 100644
--- a/static/components/vevent-dl.ts
+++ b/static/ts/components/vevent-dl.ts
@@ -1,3 +1,15 @@
+/**
+ * `<vevent-dl />`
+ *
+ * A description list of a vevent, used for debugging.
+ *
+ * No guarantees are given about the contents of the data fields, more
+ * than that they are related to the value in question.
+ *
+ * @category Web Components
+ * @mergeTarget components
+ * @module
+ */
export { VEventDL }
import { ComponentVEvent } from './vevent'
diff --git a/static/components/vevent-edit.ts b/static/ts/components/vevent-edit.ts
index e3b5d105..5dd39ee9 100644
--- a/static/components/vevent-edit.ts
+++ b/static/ts/components/vevent-edit.ts
@@ -1,3 +1,14 @@
+/**
+ * `<vevent-edit />`
+ *
+ * Edit form for a vevent, designed for useful human interaction (and
+ * thereby not being all-encompassing).
+ *
+ * @category Web Components
+ * @mergeTarget components
+ * @module
+ */
+
export { ComponentEdit }
import { ComponentVEvent } from './vevent'
diff --git a/static/components/vevent.ts b/static/ts/components/vevent.ts
index 7487cbb6..50ff4a30 100644
--- a/static/components/vevent.ts
+++ b/static/ts/components/vevent.ts
@@ -1,18 +1,48 @@
+/**
+ * Root component for all events which content is closely linked to a `VEvent` object
+ *
+ * Lacks an accompaning tag, and shouldn't be directly instanciated.
+ *
+ * Note that many of these assume that their initial children are
+ * configured specifically, that is however not completely documented.
+ *
+ * @category Web Components
+ * @mergeTarget components
+ * @module
+ */
+
export { ComponentVEvent }
import { vcal_objects } from '../globals'
import { VEvent } from '../vevent'
-/* Root component for all events which content is closely linked to a
-@code{VEvent} object
+/**
+ Base class for all Web Components closely linked with VEvents.
+
+ TODO document how templates work.
-Lacks an accompaning tag, and shouldn't be directly instanciated.
-*/
+ TODO document lifecycle, and how objects are fetched from the "global" store.
+ */
abstract class ComponentVEvent extends HTMLElement {
+ /**
+ The template for this event.
+
+ TODO document how this is populate
+ */
template?: HTMLTemplateElement
+
+ /** The UID of the VEvent we are tracking */
uid: string
+ /**
+ * This registeres itself, but doesn't redraw
+ * We do however redraw in connectedCallback
+
+ * @privateRemarks
+ * TODO what is done in the default constructor,
+ * and the default connectedCallback
+ */
constructor(uid?: string) {
super();
this.template = document.getElementById(this.tagName.toLowerCase()) as HTMLTemplateElement | undefined
@@ -56,6 +86,11 @@ abstract class ComponentVEvent extends HTMLElement {
should take care of that some other way */
}
+ /**
+ Called when the component is mounted.
+
+ Redraws the target if the wanted object is available at that time.
+ */
connectedCallback() {
let uid = this.dataset.uid
if (uid) {
@@ -64,6 +99,7 @@ abstract class ComponentVEvent extends HTMLElement {
}
}
+ /** While abstract for this, @emph{must} be overridden for everyone else */
abstract redraw(data: VEvent): void
}