Updated NJET

This commit is contained in:
retoor 2025-06-10 11:36:09 +02:00
parent 62eb1060d9
commit fbd72f727a

473
src/snek/static/njet.js Normal file
View File

@ -0,0 +1,473 @@
class RestClient {
constructor({ baseURL = '', headers = {} } = {}) {
this.baseURL = baseURL;
this.headers = { ...headers };
// Interceptor containers
this.interceptors = {
request: {
handlers: [],
use: (fn) => {
this.interceptors.request.handlers.push(fn);
}
},
response: {
handlers: [],
use: (successFn, errorFn) => {
this.interceptors.response.handlers.push({ success: successFn, error: errorFn });
}
}
};
}
// Core request method
request(config) {
// Merge defaults
const cfg = {
method: 'GET',
url: '',
data: null,
headers: {},
...config
};
cfg.headers = { ...this.headers, ...cfg.headers };
// Apply request interceptors
let chain = Promise.resolve(cfg);
this.interceptors.request.handlers.forEach((fn) => {
chain = chain.then(fn);
});
// Perform fetch
chain = chain.then((c) => {
const url = this.baseURL + c.url;
const options = { method: c.method, headers: c.headers };
if (c.data != null) {
options.body = JSON.stringify(c.data);
if (!options.headers['Content-Type']) {
options.headers['Content-Type'] = 'application/json';
}
}
return fetch(url, options).then(async (response) => {
const text = await response.text();
let data;
try {
data = JSON.parse(text);
} catch (e) {
data = text;
}
const result = {
data,
status: response.status,
statusText: response.statusText,
headers: RestClient._parseHeaders(response.headers),
config: c,
request: response
};
if (!response.ok) {
return Promise.reject(result);
}
return result;
});
});
// Apply response interceptors
this.interceptors.response.handlers.forEach(({ success, error }) => {
chain = chain.then(success, error);
});
return chain;
}
// Helper methods for HTTP verbs
get(url, config) {
return this.request({ ...config, method: 'GET', url });
}
delete(url, config) {
return this.request({ ...config, method: 'DELETE', url });
}
head(url, config) {
return this.request({ ...config, method: 'HEAD', url });
}
options(url, config) {
return this.request({ ...config, method: 'OPTIONS', url });
}
post(url, data, config) {
return this.request({ ...config, method: 'POST', url, data });
}
put(url, data, config) {
return this.request({ ...config, method: 'PUT', url, data });
}
patch(url, data, config) {
return this.request({ ...config, method: 'PATCH', url, data });
}
// Utility to parse Fetch headers into an object
static _parseHeaders(headers) {
const result = {};
for (const [key, value] of headers.entries()) {
result[key] = value;
}
return result;
}
}
class Njet extends HTMLElement {
static _root = null
static showDialog = null
get isRoot() {
return Njet._root === this
}
get root() {
return Njet._root
}
get rest() {
return Njet._root._rest
}
_subscriptions = {}
_elements = []
_rest = null
match(args) {
return Object.entries(args).every(([key, value]) => this[key] === value);
}
set(key, value) {
this.dataset[key] = value
}
get(key, defaultValue) {
if (this.dataset[key]) {
return this.dataset[key]
}
if (defaultValue === undefined) {
return
}
this.dataset[key] = defaultValue
return this.dataset[key]
}
showDialog(args){
// const dialog = this.createComponent('njet-dialog', args)
// dialog.show()
// return dialog()
}
find(args) {
for (let element of this.root._elements) {
if (element.match(args)) {
return element
}
}
return null
}
findAll(args) {
return this.root._elements.filter(element => element.match(args))
}
subscribe(event, callback) {
if (!this.root._subscriptions[event]) {
this.root._subscriptions[event] = []
}
this.root._subscriptions[event].push(callback)
}
publish(event, data) {
if (this.root._subscriptions[event]) {
this.root._subscriptions[event].forEach(callback => callback(data))
}
}
static registerComponent(name, component) {
customElements.define(name, component);
}
constructor(config = {}) {
super();
if (!Njet._root) {
Njet._root = this
Njet._rest = new RestClient({ baseURL: config.baseURL || null })
}
this.root._elements.push(this)
this.classList.add('njet');
this.config = config;
this.render.call(this);
this.initProps(config);
if (typeof this.construct === 'function')
this.construct.call(this)
}
initProps(config) {
const props = Object.keys(config)
props.forEach(prop => {
if (config[prop] !== undefined) {
this[prop] = config[prop];
}
});
if (config.classes) {
this.classList.add(...config.classes);
}
}
duplicate() {
const duplicatedConfig = { ...this.config };
if (duplicatedConfig.items) {
duplicatedConfig.items = duplicatedConfig.items.map(item => {
return typeof item.duplicate === 'function' ? item.duplicate() : item;
});
}
return new this.constructor(duplicatedConfig);
}
set width(val) {
this.style.width = typeof val === 'number' ? `${val}px` : val;
}
get width() { return this.style.width; }
set height(val) {
this.style.height = typeof val === 'number' ? `${val}px` : val;
}
get height() { return this.style.height; }
set left(val) {
this.style.position = 'absolute';
this.style.left = typeof val === 'number' ? `${val}px` : val;
}
get left() { return this.style.left; }
set top(val) {
this.style.position = 'absolute';
this.style.top = typeof val === 'number' ? `${val}px` : val;
}
get top() { return this.style.top; }
set opacity(val) { this.style.opacity = val; }
get opacity() { return this.style.opacity; }
set disabled(val) { this.toggleAttribute('disabled', !!val); }
get disabled() { return this.hasAttribute('disabled'); }
set visible(val) { this.style.display = val ? '' : 'none'; }
get visible() { return this.style.display !== 'none'; }
render() {}
}
class Component extends Njet {}
class NjetPanel extends Component {
render() {
this.innerHTML = '';
const { title, items = [] } = this.config;
this.style.border = '1px solid #ccc';
this.style.padding = '10px';
if (title) {
const header = document.createElement('h3');
header.textContent = title;
this.appendChild(header);
}
items.forEach(item => this.appendChild(item));
}
}
Njet.registerComponent('njet-panel', NjetPanel);
class NjetButton extends Component {
render() {
this.classList.add('njet-button');
this.innerHTML = '';
const button = document.createElement('button');
button.textContent = this.config.text || 'Button';
if (typeof this.config.handler === 'function') {
button.addEventListener('click', (event) => this.config.handler.call(this));
}
const observer = new MutationObserver(() => {
button.disabled = this.disabled;
});
observer.observe(this, { attributes: true, attributeFilter: ['disabled'] });
button.disabled = this.disabled;
this.appendChild(button);
}
}
Njet.registerComponent('njet-button', NjetButton);
class NjetDialog extends Component {
render() {
this.innerHTML = '';
const { title, content, primaryButton, secondaryButton } = this.config;
this.classList.add('njet-dialog');
this.style.position = 'fixed';
this.style.top = '50%';
this.style.left = '50%';
this.style.transform = 'translate(-50%, -50%)';
this.style.padding = '20px';
this.style.border = '1px solid #444';
this.style.backgroundColor = '#fff';
this.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.2)';
this.style.minWidth = '300px';
if (title) {
const header = document.createElement('h2');
header.textContent = title;
this.appendChild(header);
}
if (content) {
const body = document.createElement('div');
body.innerHTML = content;
this.appendChild(body);
}
const buttonContainer = document.createElement('div');
buttonContainer.style.marginTop = '20px';
buttonContainer.style.display = 'flex';
buttonContainer.style.justifyContent = 'flenjet-end';
buttonContainer.style.gap = '10px';
if (secondaryButton) {
const secondary = new NjetButton(secondaryButton);
buttonContainer.appendChild(secondary);
}
if (primaryButton) {
const primary = new NjetButton(primaryButton);
buttonContainer.appendChild(primary);
}
this.appendChild(buttonContainer);
}
show(){
document.body.appendChild(this)
}
}
Njet.registerComponent('njet-dialog', NjetDialog);
class NjetGrid extends Component {
render() {
this.classList.add('njet-grid');
this.innerHTML = '';
const table = document.createElement('table');
table.style.width = '100%';
table.style.borderCollapse = 'collapse';
const data = this.config.data || [];
data.forEach(row => {
const tr = document.createElement('tr');
Object.values(row).forEach(cell => {
const td = document.createElement('td');
td.textContent = cell;
td.style.border = '1px solid #ddd';
td.style.padding = '4px';
tr.appendChild(td);
});
table.appendChild(tr);
});
this.appendChild(table);
}
}
Njet.registerComponent('njet-grid', NjetGrid);
/*
const button = new NjetButton({
classes: ['my-button'],
text: 'Shared',
tag: 'shared',
width: 120,
height: 30,
handler() {
this.root.findAll({ tag: 'shared' }).forEach(e => {
e.disabled = !e.disabled;
});
}
});
const button2 = new NjetButton({
classes: ['my-button'],
text: 'Single',
iValue: 0,
width: 120,
height: 30,
handler() {
this.iValue++;
const panel = this.closest('njet-panel');
if (panel) {
const h3 = panel.querySelector('h3');
if (h3) h3.textContent = `Click ${this.iValue}`;
}
this.publish("btn2Click", `Click ${this.iValue}`);
}
});
const grid = new NjetGrid({
data: [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' }
],
width: '100%',
visible: true
});
const panel = new NjetPanel({
title: 'My Panel',
items: [button, grid, button2],
left: 50,
top: 50,
construct: function () {
this.subscribe('btn2Click', (data) => {
this._title = data
});
}
});
document.body.appendChild(panel);
const panelClone = panel.duplicate();
const panell = panelClone.duplicate();
panell.left = 120;
panell.width = 300;
panelClone.appendChild(panell);
panelClone.left = 300;
panelClone.top = 50;
document.body.appendChild(panelClone);
const dialog = new NjetDialog({
title: 'Confirm Action',
content: 'Are you sure you want to continue?',
primaryButton: {
text: 'Yes',
handler: function () {
alert('Confirmed');
this.closest('njet-dialog').remove();
}
},
secondaryButton: {
text: 'Cancel',
handler: function () {
this.closest('njet-dialog').remove();
}
}
});
document.body.appendChild(dialog);
*/
class NjetComponent extends Component {}
const njet = Njet;
njet.showDialog = function(args){
const dialog = new NjetDialog(args)
dialog.show()
return dialog
}
window.njet = njet
export { Njet, NjetButton, NjetPanel, NjetDialog, NjetGrid, NjetComponent, njet};