Skip to content
Snippets Groups Projects
Verified Commit 3b9b2861 authored by Volker Schukai's avatar Volker Schukai :alien:
Browse files

feat: new calendar control #296

parent 3883f229
No related branches found
No related tags found
No related merge requests found
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>new calendar control #296</title>
<script src="./296.mjs" type="module"></script>
</head>
<body>
<h1>new calendar control #296</h1>
<p></p>
<ul>
<li><a href="https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/296">Issue #296</a></li>
<li><a href="/">Back to overview</a></li>
</ul>
<main>
<!-- Write your code here -->
</main>
</body>
</html>
/**
* @file development/issues/open/296.mjs
* @url https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/296
* @description new calendar control
* @issue 296
*/
import "../../../source/components/style/property.pcss";
import "../../../source/components/style/link.pcss";
import "../../../source/components/style/color.pcss";
import "../../../source/components/style/theme.pcss";
import "../../../source/components/style/normalize.pcss";
import "../../../source/components/style/typography.pcss";
import {resolve} from 'path';
import {defineConfig} from 'vite'
import {projectRoot} from './import.mjs';
......@@ -15,8 +14,97 @@ import postcssResponsiveType from 'postcss-responsive-type';
import {ViteMinifyPlugin} from 'vite-plugin-minify'
import {viteMockServe} from 'vite-plugin-mock';
import fs from 'fs';
import path from 'path';
import {buildCSS, createScriptFilenameFromStyleFilename} from '../scripts/buildStylePostCSS.mjs';
function createIssueDirectoryListingPlugin() {
return {
name: 'create-issue-directory-listing-plugin',
configureServer(server) {
server.middlewares.use((req, res, next) => {
const urlPath = decodeURIComponent(req.url.split('?')[0]); // Verhindert Invalid URL Fehler
const pp = path.join(server.config.root, '/development/issues');
const fsPath = path.join(pp, urlPath);
if (path.extname(urlPath)) {
if (urlPath.endsWith('.html') || urlPath.match(/\/(open|closed)\/\d+\.m?js$/)) {
req.url = '/development/issues' + req.url;
next();
return;
}
next();
return;
}
if (fs.existsSync(fsPath) && fs.statSync(fsPath).isDirectory()) {
const files = fs.readdirSync(fsPath);
// filter only html files and all directories
const htmlAndDirectories = files.filter(file => {
return fs.statSync(path.join(fsPath, file)).isDirectory() || file.endsWith('.html');
});
// Korrekte Erstellung der Liste mit absoluten URLs
const listItems = htmlAndDirectories.map(file => {
const filePath = path.posix.join(urlPath, file);
const fullFilePath = path.join(fsPath, file);
// Check if the file is an HTML file to extract title
if (file.endsWith('.html')) {
const content = fs.readFileSync(fullFilePath, 'utf8');
const titleMatch = content.match(/<title>(.*?)<\/title>/);
const title = titleMatch ? titleMatch[1] : file; // Use the file name if no title is found
return `<li><a href="`+filePath+`">`+title+`</a></li>`;
} else {
return `<li><a href="`+filePath+`">`+file+`</a></li>`;
}
}).join('');
let homeButton = '';
if (urlPath !== '/') {
homeButton = `<a href="/">🏠 Home</a>`;
}
// Sicherstellen, dass das HTML korrekt zurückgegeben wird
const html = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Index of ${urlPath}</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
ul { list-style-type: none; padding: 0; }
li { margin: 5px 0; }
a { text-decoration: none; color: #007bff; }
a:hover { text-decoration: underline; }
</style>
</head>
<body>
` + homeButton + `
<h1>Index of ` + urlPath + `</h1>
<ul>` + listItems + `</ul>
</body>
</html>
`;
res.setHeader('Content-Type', 'text/html');
res.end(html);
} else {
next();
}
});
},
};
}
export default defineConfig({
clearScreen: false,
......@@ -41,6 +129,8 @@ export default defineConfig({
ViteMinifyPlugin(),
//directoryIndex({}),
createIssueDirectoryListingPlugin(),
viteMockServe({
mockPath: projectRoot + '/development/mock',
}),
......
/**
* Copyright © schukai GmbH and all contributing authors, {{copyRightYear}}. All rights reserved.
* Node module: @schukai/monster
*
* This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
* The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html
*
* For those who do not wish to adhere to the AGPLv3, a commercial license is available.
* Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
* For more information about purchasing a commercial license, please contact schukai GmbH.
*/
import { instanceSymbol } from "../../constants.mjs";
import { addAttributeToken } from "../../dom/attributes.mjs";
import {
ATTRIBUTE_ERRORMESSAGE,
ATTRIBUTE_ROLE,
} from "../../dom/constants.mjs";
import { CustomControl } from "../../dom/customcontrol.mjs";
import { CustomElement } from "../../dom/customelement.mjs";
import {
assembleMethodSymbol,
registerCustomElement,
} from "../../dom/customelement.mjs";
import { findTargetElementFromEvent } from "../../dom/events.mjs";
import { isFunction } from "../../types/is.mjs";
import { CalendarStyleSheet } from "./stylesheet/calendar.mjs";
import { fireCustomEvent } from "../../dom/events.mjs";
export { Calendar };
/**
* @private
* @type {symbol}
*/
export const calendarElementSymbol = Symbol("calendarElement");
/**
* A Calendar
*
* @fragments /fragments/components/time/calendar/
*
* @example /examples/components/time/calendar-simple
*
* @since 3.112.0
* @copyright schukai GmbH
* @summary A beautiful Calendar that can make your life easier and also looks good.
*/
class Calendar extends CustomElement {
/**
* This method is called by the `instanceof` operator.
* @returns {symbol}
*/
static get [instanceSymbol]() {
return Symbol.for("@schukai/monster/components/time/calendar@@instance");
}
/**
*
* @return {Components.Time.Calendar
*/
[assembleMethodSymbol]() {
super[assembleMethodSymbol]();
initControlReferences.call(this);
initEventHandler.call(this);
return this;
}
/**
* To set the options via the HTML Tag, the attribute `data-monster-options` must be used.
* @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
*
* The individual configuration values can be found in the table.
*
* @property {Object} templates Template definitions
* @property {string} templates.main Main template
* @property {Object} labels Label definitions
* @property {Object} actions Callbacks
* @property {string} actions.click="throw Error" Callback when clicked
* @property {Object} features Features
* @property {Object} classes CSS classes
* @property {boolean} disabled=false Disabled state
*/
get defaults() {
return Object.assign({}, super.defaults, {
templates: {
main: getTemplate(),
},
labels: {
},
classes: {
},
disabled: false,
features: {
},
actions: {
click: () => {
throw new Error("the click action is not defined");
},
}
});
}
/**
* @return {string}
*/
static getTag() {
return "monster-calendar";
}
/**
* @return {CSSStyleSheet[]}
*/
static getCSSStyleSheet() {
return [CalendarStyleSheet];
}
}
/**
* @private
* @return {initEventHandler}
* @fires monster-calendar-clicked
*/
function initEventHandler() {
const self = this;
const element = this[calendarElementSymbol];
const type = "click";
element.addEventListener(type, function (event) {
const callback = self.getOption("actions.click");
fireCustomEvent(self, "monster-calendar-clicked", {
element: self,
});
if (!isFunction(callback)) {
return;
}
const element = findTargetElementFromEvent(
event,
ATTRIBUTE_ROLE,
"control",
);
if (!(element instanceof Node && self.hasNode(element))) {
return;
}
callback.call(self, event);
});
return this;
}
/**
* @private
* @return {void}
*/
function initControlReferences() {
this[calendarElementSymbol] = this.shadowRoot.querySelector(
`[${ATTRIBUTE_ROLE}="control"]`,
);
}
/**
* @private
* @return {string}
*/
function getTemplate() {
// language=HTML
return `
<div data-monster-role="control" part="control">
</div>`;
}
registerCustomElement(Calendar);
/**
* Copyright © schukai GmbH and all contributing authors, 2025. All rights reserved.
* Node module: @schukai/monster
*
* This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
* The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html
*
* For those who do not wish to adhere to the AGPLv3, a commercial license is available.
* Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
* For more information about purchasing a commercial license, please contact schukai GmbH.
*/
import {addAttributeToken} from "../../../dom/attributes.mjs";
import {ATTRIBUTE_ERRORMESSAGE} from "../../../dom/constants.mjs";
export {CalendarStyleSheet}
/**
* @private
* @type {CSSStyleSheet}
*/
const CalendarStyleSheet = new CSSStyleSheet();
try {
CalendarStyleSheet.insertRule(`
@layer calendar {
}`, 0);
} catch (e) {
addAttributeToken(document.getRootNode().querySelector('html'), ATTRIBUTE_ERRORMESSAGE, e + "");
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment