You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nostrweb/src/view.ts

93 lines
2.7 KiB
TypeScript

import {DOMMap, renderViewTemplate, ViewTemplateOptions} from './template';
type Container = {
id: string;
options: ViewTemplateOptions,
view: HTMLElement;
content: HTMLDivElement;
dom: DOMMap;
};
const containers: Array<Container> = [];
let activeContainerIndex = -1;
export const getViewContent = () => containers[activeContainerIndex]?.content;
/**
* clears current view so it is empty and ready to be re-used.
* only clears the current view, not all views
*/
export const clearView = () => {
const domMap = containers[activeContainerIndex]?.dom;
Object.keys(domMap).forEach(eventId => delete domMap[eventId]);
getViewContent().replaceChildren();
};
/**
* get elmenet stored in internal DOMMap of the current view
* alternative to internal map in view.dom, is to use id="" attribute, however same event could be shown in different views so event.id is not unique.
*/
export const getViewElem = (id: string) => {
return containers[activeContainerIndex]?.dom[id];
};
/**
* store element in internal view.dom map using id as key
*/
export const setViewElem = (id: string, node: HTMLElement) => {
const container = containers[activeContainerIndex];
if (container) {
container.dom[id] = node;
}
return node;
};
const mainContainer = document.querySelector('main') as HTMLElement;
const createContainer = (
route: string,
options: ViewTemplateOptions,
) => {
const {content, dom, view} = renderViewTemplate(options);
const container = {id: route, options, view, content, dom};
mainContainer.append(view);
containers.push(container);
return container;
};
type GetViewOptions = () => ViewTemplateOptions;
/**
* get options for current view
* @returns {id: 'home' | 'feed' | 'profile' | 'note' | 'contacts' | 'event', id?: string}
*/
export const getViewOptions: GetViewOptions = () => containers[activeContainerIndex]?.options || {type: 'feed'};
/**
* changes the current view and transitions to the view specified by route
* example:
* view('/npub0293ji3gojaed32r4r412', {type: 'feed})
*/
export const view = (
route: string,
options: ViewTemplateOptions,
) => {
const active = containers[activeContainerIndex];
const nextContainer = containers.find(c => c.id === route) || createContainer(route, options);
const nextContainerIndex = containers.indexOf(nextContainer);
if (nextContainerIndex === activeContainerIndex) {
return;
}
if (active) {
nextContainer.view.classList.add('view-next');
}
requestAnimationFrame(() => {
requestAnimationFrame(() => {
nextContainer.view.classList.remove('view-next', 'view-prev');
});
active?.view.classList.add(nextContainerIndex < activeContainerIndex ? 'view-next' : 'view-prev');
activeContainerIndex = nextContainerIndex;
});
};