From 200058ba03d421b2b728582200dba544369de453 Mon Sep 17 00:00:00 2001 From: Daniel Legt Date: Fri, 27 Feb 2026 11:39:29 +0200 Subject: [PATCH] Initial Commit --- .gitignore | 24 ++++ css/base/alert.css | 70 ++++++++++ css/base/base.css | 31 +++++ css/base/buttons.css | 27 ++++ css/themes/generic/theme.css | 5 + css/themes/win98/theme.css | 11 ++ css/themes/winxp/theme.css | 12 ++ dist/y2k-alerts-generic.css | 139 +++++++++++++++++++ dist/y2k-alerts-generic.min.css | 1 + dist/y2k-alerts-win98.css | 145 ++++++++++++++++++++ dist/y2k-alerts-win98.min.css | 1 + dist/y2k-alerts-winxp.css | 146 ++++++++++++++++++++ dist/y2k-alerts-winxp.min.css | 1 + dist/y2k-alerts.css | 139 +++++++++++++++++++ dist/y2k-alerts.js | 229 ++++++++++++++++++++++++++++++++ dist/y2k-alerts.min.css | 1 + dist/y2k-alerts.min.js | 1 + examples/generic.html | 71 ++++++++++ examples/win98.html | 30 +++++ examples/winxp.html | 41 ++++++ js/constants.js | 11 ++ js/dom.js | 30 +++++ js/index.js | 11 ++ js/y2k-alert.js | 171 ++++++++++++++++++++++++ package.json | 6 +- scripts/build.js | 93 +++++++++++++ 26 files changed, 1445 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 css/base/alert.css create mode 100644 css/base/base.css create mode 100644 css/base/buttons.css create mode 100644 css/themes/generic/theme.css create mode 100644 css/themes/win98/theme.css create mode 100644 css/themes/winxp/theme.css create mode 100644 dist/y2k-alerts-generic.css create mode 100644 dist/y2k-alerts-generic.min.css create mode 100644 dist/y2k-alerts-win98.css create mode 100644 dist/y2k-alerts-win98.min.css create mode 100644 dist/y2k-alerts-winxp.css create mode 100644 dist/y2k-alerts-winxp.min.css create mode 100644 dist/y2k-alerts.css create mode 100644 dist/y2k-alerts.js create mode 100644 dist/y2k-alerts.min.css create mode 100644 dist/y2k-alerts.min.js create mode 100644 examples/generic.html create mode 100644 examples/win98.html create mode 100644 examples/winxp.html create mode 100644 js/constants.js create mode 100644 js/dom.js create mode 100644 js/index.js create mode 100644 js/y2k-alert.js create mode 100644 scripts/build.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57a0012 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Dependencies +node_modules/ + +# Logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Environment files +.env +.env.* +!.env.example + +# Coverage and temp output +coverage/ +*.tmp +*.temp + +# OS / editor files +.DS_Store +Thumbs.db +.vscode/ +.idea/ diff --git a/css/base/alert.css b/css/base/alert.css new file mode 100644 index 0000000..45ca667 --- /dev/null +++ b/css/base/alert.css @@ -0,0 +1,70 @@ +.y2k-alert { + width: min(92vw, 360px); + color: var(--y2k-text); + background: var(--y2k-surface); + border-style: solid; + border-width: 2px; + border-color: var(--y2k-border-light) var(--y2k-border-dark) var(--y2k-border-dark) var(--y2k-border-light); + box-shadow: 4px 4px 0 rgba(0, 0, 0, 0.22); + font-family: var(--y2k-font); +} + +.y2k-alert__titlebar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + padding: 8px; + color: var(--y2k-title-text); + background: linear-gradient(90deg, var(--y2k-title-start), var(--y2k-title-end)); +} + +.y2k-alert__title { + margin: 0; + font-size: 14px; + font-weight: 700; + line-height: 1; +} + +.y2k-alert__content { + padding: 12px; +} + +.y2k-alert__text { + margin: 0; + font-size: 14px; + line-height: 1.4; +} + +.y2k-alert__input { + width: 100%; + margin-top: 12px; + box-sizing: border-box; + border: 1px solid var(--y2k-border-dark); + padding: 6px 8px; + font-family: var(--y2k-font); + font-size: 14px; +} + +.y2k-alert__actions { + display: flex; + justify-content: flex-end; + gap: 8px; + margin-top: 12px; +} + +.y2k-alert--info { + border-left: 4px solid var(--y2k-type-info); +} + +.y2k-alert--warning { + border-left: 4px solid var(--y2k-type-warning); +} + +.y2k-alert--error { + border-left: 4px solid var(--y2k-type-error); +} + +.y2k-alert--success { + border-left: 4px solid var(--y2k-type-success); +} diff --git a/css/base/base.css b/css/base/base.css new file mode 100644 index 0000000..f0fff4b --- /dev/null +++ b/css/base/base.css @@ -0,0 +1,31 @@ +:root { + --y2k-font: "Tahoma", "Verdana", sans-serif; + --y2k-bg: rgba(0, 0, 0, 0.18); + --y2k-surface: #f3f3f3; + --y2k-border-light: #ffffff; + --y2k-border-dark: #5a5a5a; + --y2k-title-start: #1d4f9a; + --y2k-title-end: #3b7ad0; + --y2k-title-text: #ffffff; + --y2k-text: #111111; + --y2k-button-bg: #e3e3e3; + --y2k-button-text: #111111; + --y2k-button-border-light: #ffffff; + --y2k-button-border-dark: #6d6d6d; + --y2k-focus: #0a6cff; + --y2k-type-info: #2f5ca8; + --y2k-type-warning: #d08900; + --y2k-type-error: #b62828; + --y2k-type-success: #288c39; +} + +.y2k-alert-overlay { + position: fixed; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + padding: 16px; + background: var(--y2k-bg); + z-index: 9999; +} diff --git a/css/base/buttons.css b/css/base/buttons.css new file mode 100644 index 0000000..4892ae6 --- /dev/null +++ b/css/base/buttons.css @@ -0,0 +1,27 @@ +.y2k-btn { + border-style: solid; + border-width: 2px; + border-color: var(--y2k-button-border-light) var(--y2k-button-border-dark) var(--y2k-button-border-dark) var(--y2k-button-border-light); + background: var(--y2k-button-bg); + color: var(--y2k-button-text); + padding: 4px 12px; + font-family: var(--y2k-font); + font-size: 13px; + cursor: pointer; +} + +.y2k-btn:active { + border-color: var(--y2k-button-border-dark) var(--y2k-button-border-light) var(--y2k-button-border-light) var(--y2k-button-border-dark); +} + +.y2k-btn:focus-visible, +.y2k-alert__input:focus-visible { + outline: 2px solid var(--y2k-focus); + outline-offset: 1px; +} + +.y2k-btn--close { + min-width: 28px; + padding: 2px 8px; + line-height: 1; +} diff --git a/css/themes/generic/theme.css b/css/themes/generic/theme.css new file mode 100644 index 0000000..a5eec82 --- /dev/null +++ b/css/themes/generic/theme.css @@ -0,0 +1,5 @@ +:root { + --y2k-title-start: #4457d9; + --y2k-title-end: #1a2d8f; + --y2k-bg: rgba(20, 20, 20, 0.22); +} diff --git a/css/themes/win98/theme.css b/css/themes/win98/theme.css new file mode 100644 index 0000000..a60c199 --- /dev/null +++ b/css/themes/win98/theme.css @@ -0,0 +1,11 @@ +:root { + --y2k-font: "MS Sans Serif", "Tahoma", sans-serif; + --y2k-surface: #c0c0c0; + --y2k-border-light: #ffffff; + --y2k-border-dark: #808080; + --y2k-title-start: #000080; + --y2k-title-end: #1084d0; + --y2k-title-text: #ffffff; + --y2k-button-bg: #c0c0c0; + --y2k-button-border-dark: #808080; +} diff --git a/css/themes/winxp/theme.css b/css/themes/winxp/theme.css new file mode 100644 index 0000000..735c837 --- /dev/null +++ b/css/themes/winxp/theme.css @@ -0,0 +1,12 @@ +:root { + --y2k-font: "Tahoma", "Verdana", sans-serif; + --y2k-surface: #ece9d8; + --y2k-border-light: #ffffff; + --y2k-border-dark: #7f9db9; + --y2k-title-start: #0a246a; + --y2k-title-end: #3a6ea5; + --y2k-title-text: #ffffff; + --y2k-button-bg: #f0f4ff; + --y2k-button-border-dark: #7f9db9; + --y2k-focus: #3257d6; +} diff --git a/dist/y2k-alerts-generic.css b/dist/y2k-alerts-generic.css new file mode 100644 index 0000000..11d60c4 --- /dev/null +++ b/dist/y2k-alerts-generic.css @@ -0,0 +1,139 @@ +:root { + --y2k-font: "Tahoma", "Verdana", sans-serif; + --y2k-bg: rgba(0, 0, 0, 0.18); + --y2k-surface: #f3f3f3; + --y2k-border-light: #ffffff; + --y2k-border-dark: #5a5a5a; + --y2k-title-start: #1d4f9a; + --y2k-title-end: #3b7ad0; + --y2k-title-text: #ffffff; + --y2k-text: #111111; + --y2k-button-bg: #e3e3e3; + --y2k-button-text: #111111; + --y2k-button-border-light: #ffffff; + --y2k-button-border-dark: #6d6d6d; + --y2k-focus: #0a6cff; + --y2k-type-info: #2f5ca8; + --y2k-type-warning: #d08900; + --y2k-type-error: #b62828; + --y2k-type-success: #288c39; +} + +.y2k-alert-overlay { + position: fixed; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + padding: 16px; + background: var(--y2k-bg); + z-index: 9999; +} + + +.y2k-alert { + width: min(92vw, 360px); + color: var(--y2k-text); + background: var(--y2k-surface); + border-style: solid; + border-width: 2px; + border-color: var(--y2k-border-light) var(--y2k-border-dark) var(--y2k-border-dark) var(--y2k-border-light); + box-shadow: 4px 4px 0 rgba(0, 0, 0, 0.22); + font-family: var(--y2k-font); +} + +.y2k-alert__titlebar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + padding: 8px; + color: var(--y2k-title-text); + background: linear-gradient(90deg, var(--y2k-title-start), var(--y2k-title-end)); +} + +.y2k-alert__title { + margin: 0; + font-size: 14px; + font-weight: 700; + line-height: 1; +} + +.y2k-alert__content { + padding: 12px; +} + +.y2k-alert__text { + margin: 0; + font-size: 14px; + line-height: 1.4; +} + +.y2k-alert__input { + width: 100%; + margin-top: 12px; + box-sizing: border-box; + border: 1px solid var(--y2k-border-dark); + padding: 6px 8px; + font-family: var(--y2k-font); + font-size: 14px; +} + +.y2k-alert__actions { + display: flex; + justify-content: flex-end; + gap: 8px; + margin-top: 12px; +} + +.y2k-alert--info { + border-left: 4px solid var(--y2k-type-info); +} + +.y2k-alert--warning { + border-left: 4px solid var(--y2k-type-warning); +} + +.y2k-alert--error { + border-left: 4px solid var(--y2k-type-error); +} + +.y2k-alert--success { + border-left: 4px solid var(--y2k-type-success); +} + + +.y2k-btn { + border-style: solid; + border-width: 2px; + border-color: var(--y2k-button-border-light) var(--y2k-button-border-dark) var(--y2k-button-border-dark) var(--y2k-button-border-light); + background: var(--y2k-button-bg); + color: var(--y2k-button-text); + padding: 4px 12px; + font-family: var(--y2k-font); + font-size: 13px; + cursor: pointer; +} + +.y2k-btn:active { + border-color: var(--y2k-button-border-dark) var(--y2k-button-border-light) var(--y2k-button-border-light) var(--y2k-button-border-dark); +} + +.y2k-btn:focus-visible, +.y2k-alert__input:focus-visible { + outline: 2px solid var(--y2k-focus); + outline-offset: 1px; +} + +.y2k-btn--close { + min-width: 28px; + padding: 2px 8px; + line-height: 1; +} + + +:root { + --y2k-title-start: #4457d9; + --y2k-title-end: #1a2d8f; + --y2k-bg: rgba(20, 20, 20, 0.22); +} diff --git a/dist/y2k-alerts-generic.min.css b/dist/y2k-alerts-generic.min.css new file mode 100644 index 0000000..9d652c5 --- /dev/null +++ b/dist/y2k-alerts-generic.min.css @@ -0,0 +1 @@ +:root{--y2k-font:"Tahoma","Verdana",sans-serif;--y2k-bg:rgba(0,0,0,0.18);--y2k-surface:#f3f3f3;--y2k-border-light:#ffffff;--y2k-border-dark:#5a5a5a;--y2k-title-start:#1d4f9a;--y2k-title-end:#3b7ad0;--y2k-title-text:#ffffff;--y2k-text:#111111;--y2k-button-bg:#e3e3e3;--y2k-button-text:#111111;--y2k-button-border-light:#ffffff;--y2k-button-border-dark:#6d6d6d;--y2k-focus:#0a6cff;--y2k-type-info:#2f5ca8;--y2k-type-warning:#d08900;--y2k-type-error:#b62828;--y2k-type-success:#288c39}.y2k-alert-overlay{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;padding:16px;background:var(--y2k-bg);z-index:9999}.y2k-alert{width:min(92vw,360px);color:var(--y2k-text);background:var(--y2k-surface);border-style:solid;border-width:2px;border-color:var(--y2k-border-light) var(--y2k-border-dark) var(--y2k-border-dark) var(--y2k-border-light);box-shadow:4px 4px 0 rgba(0,0,0,0.22);font-family:var(--y2k-font)}.y2k-alert__titlebar{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px;color:var(--y2k-title-text);background:linear-gradient(90deg,var(--y2k-title-start),var(--y2k-title-end))}.y2k-alert__title{margin:0;font-size:14px;font-weight:700;line-height:1}.y2k-alert__content{padding:12px}.y2k-alert__text{margin:0;font-size:14px;line-height:1.4}.y2k-alert__input{width:100%;margin-top:12px;box-sizing:border-box;border:1px solid var(--y2k-border-dark);padding:6px 8px;font-family:var(--y2k-font);font-size:14px}.y2k-alert__actions{display:flex;justify-content:flex-end;gap:8px;margin-top:12px}.y2k-alert--info{border-left:4px solid var(--y2k-type-info)}.y2k-alert--warning{border-left:4px solid var(--y2k-type-warning)}.y2k-alert--error{border-left:4px solid var(--y2k-type-error)}.y2k-alert--success{border-left:4px solid var(--y2k-type-success)}.y2k-btn{border-style:solid;border-width:2px;border-color:var(--y2k-button-border-light) var(--y2k-button-border-dark) var(--y2k-button-border-dark) var(--y2k-button-border-light);background:var(--y2k-button-bg);color:var(--y2k-button-text);padding:4px 12px;font-family:var(--y2k-font);font-size:13px;cursor:pointer}.y2k-btn:active{border-color:var(--y2k-button-border-dark) var(--y2k-button-border-light) var(--y2k-button-border-light) var(--y2k-button-border-dark)}.y2k-btn:focus-visible,.y2k-alert__input:focus-visible{outline:2px solid var(--y2k-focus);outline-offset:1px}.y2k-btn--close{min-width:28px;padding:2px 8px;line-height:1}:root{--y2k-title-start:#4457d9;--y2k-title-end:#1a2d8f;--y2k-bg:rgba(20,20,20,0.22)} \ No newline at end of file diff --git a/dist/y2k-alerts-win98.css b/dist/y2k-alerts-win98.css new file mode 100644 index 0000000..2befb0c --- /dev/null +++ b/dist/y2k-alerts-win98.css @@ -0,0 +1,145 @@ +:root { + --y2k-font: "Tahoma", "Verdana", sans-serif; + --y2k-bg: rgba(0, 0, 0, 0.18); + --y2k-surface: #f3f3f3; + --y2k-border-light: #ffffff; + --y2k-border-dark: #5a5a5a; + --y2k-title-start: #1d4f9a; + --y2k-title-end: #3b7ad0; + --y2k-title-text: #ffffff; + --y2k-text: #111111; + --y2k-button-bg: #e3e3e3; + --y2k-button-text: #111111; + --y2k-button-border-light: #ffffff; + --y2k-button-border-dark: #6d6d6d; + --y2k-focus: #0a6cff; + --y2k-type-info: #2f5ca8; + --y2k-type-warning: #d08900; + --y2k-type-error: #b62828; + --y2k-type-success: #288c39; +} + +.y2k-alert-overlay { + position: fixed; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + padding: 16px; + background: var(--y2k-bg); + z-index: 9999; +} + + +.y2k-alert { + width: min(92vw, 360px); + color: var(--y2k-text); + background: var(--y2k-surface); + border-style: solid; + border-width: 2px; + border-color: var(--y2k-border-light) var(--y2k-border-dark) var(--y2k-border-dark) var(--y2k-border-light); + box-shadow: 4px 4px 0 rgba(0, 0, 0, 0.22); + font-family: var(--y2k-font); +} + +.y2k-alert__titlebar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + padding: 8px; + color: var(--y2k-title-text); + background: linear-gradient(90deg, var(--y2k-title-start), var(--y2k-title-end)); +} + +.y2k-alert__title { + margin: 0; + font-size: 14px; + font-weight: 700; + line-height: 1; +} + +.y2k-alert__content { + padding: 12px; +} + +.y2k-alert__text { + margin: 0; + font-size: 14px; + line-height: 1.4; +} + +.y2k-alert__input { + width: 100%; + margin-top: 12px; + box-sizing: border-box; + border: 1px solid var(--y2k-border-dark); + padding: 6px 8px; + font-family: var(--y2k-font); + font-size: 14px; +} + +.y2k-alert__actions { + display: flex; + justify-content: flex-end; + gap: 8px; + margin-top: 12px; +} + +.y2k-alert--info { + border-left: 4px solid var(--y2k-type-info); +} + +.y2k-alert--warning { + border-left: 4px solid var(--y2k-type-warning); +} + +.y2k-alert--error { + border-left: 4px solid var(--y2k-type-error); +} + +.y2k-alert--success { + border-left: 4px solid var(--y2k-type-success); +} + + +.y2k-btn { + border-style: solid; + border-width: 2px; + border-color: var(--y2k-button-border-light) var(--y2k-button-border-dark) var(--y2k-button-border-dark) var(--y2k-button-border-light); + background: var(--y2k-button-bg); + color: var(--y2k-button-text); + padding: 4px 12px; + font-family: var(--y2k-font); + font-size: 13px; + cursor: pointer; +} + +.y2k-btn:active { + border-color: var(--y2k-button-border-dark) var(--y2k-button-border-light) var(--y2k-button-border-light) var(--y2k-button-border-dark); +} + +.y2k-btn:focus-visible, +.y2k-alert__input:focus-visible { + outline: 2px solid var(--y2k-focus); + outline-offset: 1px; +} + +.y2k-btn--close { + min-width: 28px; + padding: 2px 8px; + line-height: 1; +} + + +:root { + --y2k-font: "MS Sans Serif", "Tahoma", sans-serif; + --y2k-surface: #c0c0c0; + --y2k-border-light: #ffffff; + --y2k-border-dark: #808080; + --y2k-title-start: #000080; + --y2k-title-end: #1084d0; + --y2k-title-text: #ffffff; + --y2k-button-bg: #c0c0c0; + --y2k-button-border-dark: #808080; +} diff --git a/dist/y2k-alerts-win98.min.css b/dist/y2k-alerts-win98.min.css new file mode 100644 index 0000000..bd91547 --- /dev/null +++ b/dist/y2k-alerts-win98.min.css @@ -0,0 +1 @@ +:root{--y2k-font:"Tahoma","Verdana",sans-serif;--y2k-bg:rgba(0,0,0,0.18);--y2k-surface:#f3f3f3;--y2k-border-light:#ffffff;--y2k-border-dark:#5a5a5a;--y2k-title-start:#1d4f9a;--y2k-title-end:#3b7ad0;--y2k-title-text:#ffffff;--y2k-text:#111111;--y2k-button-bg:#e3e3e3;--y2k-button-text:#111111;--y2k-button-border-light:#ffffff;--y2k-button-border-dark:#6d6d6d;--y2k-focus:#0a6cff;--y2k-type-info:#2f5ca8;--y2k-type-warning:#d08900;--y2k-type-error:#b62828;--y2k-type-success:#288c39}.y2k-alert-overlay{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;padding:16px;background:var(--y2k-bg);z-index:9999}.y2k-alert{width:min(92vw,360px);color:var(--y2k-text);background:var(--y2k-surface);border-style:solid;border-width:2px;border-color:var(--y2k-border-light) var(--y2k-border-dark) var(--y2k-border-dark) var(--y2k-border-light);box-shadow:4px 4px 0 rgba(0,0,0,0.22);font-family:var(--y2k-font)}.y2k-alert__titlebar{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px;color:var(--y2k-title-text);background:linear-gradient(90deg,var(--y2k-title-start),var(--y2k-title-end))}.y2k-alert__title{margin:0;font-size:14px;font-weight:700;line-height:1}.y2k-alert__content{padding:12px}.y2k-alert__text{margin:0;font-size:14px;line-height:1.4}.y2k-alert__input{width:100%;margin-top:12px;box-sizing:border-box;border:1px solid var(--y2k-border-dark);padding:6px 8px;font-family:var(--y2k-font);font-size:14px}.y2k-alert__actions{display:flex;justify-content:flex-end;gap:8px;margin-top:12px}.y2k-alert--info{border-left:4px solid var(--y2k-type-info)}.y2k-alert--warning{border-left:4px solid var(--y2k-type-warning)}.y2k-alert--error{border-left:4px solid var(--y2k-type-error)}.y2k-alert--success{border-left:4px solid var(--y2k-type-success)}.y2k-btn{border-style:solid;border-width:2px;border-color:var(--y2k-button-border-light) var(--y2k-button-border-dark) var(--y2k-button-border-dark) var(--y2k-button-border-light);background:var(--y2k-button-bg);color:var(--y2k-button-text);padding:4px 12px;font-family:var(--y2k-font);font-size:13px;cursor:pointer}.y2k-btn:active{border-color:var(--y2k-button-border-dark) var(--y2k-button-border-light) var(--y2k-button-border-light) var(--y2k-button-border-dark)}.y2k-btn:focus-visible,.y2k-alert__input:focus-visible{outline:2px solid var(--y2k-focus);outline-offset:1px}.y2k-btn--close{min-width:28px;padding:2px 8px;line-height:1}:root{--y2k-font:"MS Sans Serif","Tahoma",sans-serif;--y2k-surface:#c0c0c0;--y2k-border-light:#ffffff;--y2k-border-dark:#808080;--y2k-title-start:#000080;--y2k-title-end:#1084d0;--y2k-title-text:#ffffff;--y2k-button-bg:#c0c0c0;--y2k-button-border-dark:#808080} \ No newline at end of file diff --git a/dist/y2k-alerts-winxp.css b/dist/y2k-alerts-winxp.css new file mode 100644 index 0000000..645e120 --- /dev/null +++ b/dist/y2k-alerts-winxp.css @@ -0,0 +1,146 @@ +:root { + --y2k-font: "Tahoma", "Verdana", sans-serif; + --y2k-bg: rgba(0, 0, 0, 0.18); + --y2k-surface: #f3f3f3; + --y2k-border-light: #ffffff; + --y2k-border-dark: #5a5a5a; + --y2k-title-start: #1d4f9a; + --y2k-title-end: #3b7ad0; + --y2k-title-text: #ffffff; + --y2k-text: #111111; + --y2k-button-bg: #e3e3e3; + --y2k-button-text: #111111; + --y2k-button-border-light: #ffffff; + --y2k-button-border-dark: #6d6d6d; + --y2k-focus: #0a6cff; + --y2k-type-info: #2f5ca8; + --y2k-type-warning: #d08900; + --y2k-type-error: #b62828; + --y2k-type-success: #288c39; +} + +.y2k-alert-overlay { + position: fixed; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + padding: 16px; + background: var(--y2k-bg); + z-index: 9999; +} + + +.y2k-alert { + width: min(92vw, 360px); + color: var(--y2k-text); + background: var(--y2k-surface); + border-style: solid; + border-width: 2px; + border-color: var(--y2k-border-light) var(--y2k-border-dark) var(--y2k-border-dark) var(--y2k-border-light); + box-shadow: 4px 4px 0 rgba(0, 0, 0, 0.22); + font-family: var(--y2k-font); +} + +.y2k-alert__titlebar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + padding: 8px; + color: var(--y2k-title-text); + background: linear-gradient(90deg, var(--y2k-title-start), var(--y2k-title-end)); +} + +.y2k-alert__title { + margin: 0; + font-size: 14px; + font-weight: 700; + line-height: 1; +} + +.y2k-alert__content { + padding: 12px; +} + +.y2k-alert__text { + margin: 0; + font-size: 14px; + line-height: 1.4; +} + +.y2k-alert__input { + width: 100%; + margin-top: 12px; + box-sizing: border-box; + border: 1px solid var(--y2k-border-dark); + padding: 6px 8px; + font-family: var(--y2k-font); + font-size: 14px; +} + +.y2k-alert__actions { + display: flex; + justify-content: flex-end; + gap: 8px; + margin-top: 12px; +} + +.y2k-alert--info { + border-left: 4px solid var(--y2k-type-info); +} + +.y2k-alert--warning { + border-left: 4px solid var(--y2k-type-warning); +} + +.y2k-alert--error { + border-left: 4px solid var(--y2k-type-error); +} + +.y2k-alert--success { + border-left: 4px solid var(--y2k-type-success); +} + + +.y2k-btn { + border-style: solid; + border-width: 2px; + border-color: var(--y2k-button-border-light) var(--y2k-button-border-dark) var(--y2k-button-border-dark) var(--y2k-button-border-light); + background: var(--y2k-button-bg); + color: var(--y2k-button-text); + padding: 4px 12px; + font-family: var(--y2k-font); + font-size: 13px; + cursor: pointer; +} + +.y2k-btn:active { + border-color: var(--y2k-button-border-dark) var(--y2k-button-border-light) var(--y2k-button-border-light) var(--y2k-button-border-dark); +} + +.y2k-btn:focus-visible, +.y2k-alert__input:focus-visible { + outline: 2px solid var(--y2k-focus); + outline-offset: 1px; +} + +.y2k-btn--close { + min-width: 28px; + padding: 2px 8px; + line-height: 1; +} + + +:root { + --y2k-font: "Tahoma", "Verdana", sans-serif; + --y2k-surface: #ece9d8; + --y2k-border-light: #ffffff; + --y2k-border-dark: #7f9db9; + --y2k-title-start: #0a246a; + --y2k-title-end: #3a6ea5; + --y2k-title-text: #ffffff; + --y2k-button-bg: #f0f4ff; + --y2k-button-border-dark: #7f9db9; + --y2k-focus: #3257d6; +} diff --git a/dist/y2k-alerts-winxp.min.css b/dist/y2k-alerts-winxp.min.css new file mode 100644 index 0000000..98b473d --- /dev/null +++ b/dist/y2k-alerts-winxp.min.css @@ -0,0 +1 @@ +:root{--y2k-font:"Tahoma","Verdana",sans-serif;--y2k-bg:rgba(0,0,0,0.18);--y2k-surface:#f3f3f3;--y2k-border-light:#ffffff;--y2k-border-dark:#5a5a5a;--y2k-title-start:#1d4f9a;--y2k-title-end:#3b7ad0;--y2k-title-text:#ffffff;--y2k-text:#111111;--y2k-button-bg:#e3e3e3;--y2k-button-text:#111111;--y2k-button-border-light:#ffffff;--y2k-button-border-dark:#6d6d6d;--y2k-focus:#0a6cff;--y2k-type-info:#2f5ca8;--y2k-type-warning:#d08900;--y2k-type-error:#b62828;--y2k-type-success:#288c39}.y2k-alert-overlay{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;padding:16px;background:var(--y2k-bg);z-index:9999}.y2k-alert{width:min(92vw,360px);color:var(--y2k-text);background:var(--y2k-surface);border-style:solid;border-width:2px;border-color:var(--y2k-border-light) var(--y2k-border-dark) var(--y2k-border-dark) var(--y2k-border-light);box-shadow:4px 4px 0 rgba(0,0,0,0.22);font-family:var(--y2k-font)}.y2k-alert__titlebar{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px;color:var(--y2k-title-text);background:linear-gradient(90deg,var(--y2k-title-start),var(--y2k-title-end))}.y2k-alert__title{margin:0;font-size:14px;font-weight:700;line-height:1}.y2k-alert__content{padding:12px}.y2k-alert__text{margin:0;font-size:14px;line-height:1.4}.y2k-alert__input{width:100%;margin-top:12px;box-sizing:border-box;border:1px solid var(--y2k-border-dark);padding:6px 8px;font-family:var(--y2k-font);font-size:14px}.y2k-alert__actions{display:flex;justify-content:flex-end;gap:8px;margin-top:12px}.y2k-alert--info{border-left:4px solid var(--y2k-type-info)}.y2k-alert--warning{border-left:4px solid var(--y2k-type-warning)}.y2k-alert--error{border-left:4px solid var(--y2k-type-error)}.y2k-alert--success{border-left:4px solid var(--y2k-type-success)}.y2k-btn{border-style:solid;border-width:2px;border-color:var(--y2k-button-border-light) var(--y2k-button-border-dark) var(--y2k-button-border-dark) var(--y2k-button-border-light);background:var(--y2k-button-bg);color:var(--y2k-button-text);padding:4px 12px;font-family:var(--y2k-font);font-size:13px;cursor:pointer}.y2k-btn:active{border-color:var(--y2k-button-border-dark) var(--y2k-button-border-light) var(--y2k-button-border-light) var(--y2k-button-border-dark)}.y2k-btn:focus-visible,.y2k-alert__input:focus-visible{outline:2px solid var(--y2k-focus);outline-offset:1px}.y2k-btn--close{min-width:28px;padding:2px 8px;line-height:1}:root{--y2k-font:"Tahoma","Verdana",sans-serif;--y2k-surface:#ece9d8;--y2k-border-light:#ffffff;--y2k-border-dark:#7f9db9;--y2k-title-start:#0a246a;--y2k-title-end:#3a6ea5;--y2k-title-text:#ffffff;--y2k-button-bg:#f0f4ff;--y2k-button-border-dark:#7f9db9;--y2k-focus:#3257d6} \ No newline at end of file diff --git a/dist/y2k-alerts.css b/dist/y2k-alerts.css new file mode 100644 index 0000000..11d60c4 --- /dev/null +++ b/dist/y2k-alerts.css @@ -0,0 +1,139 @@ +:root { + --y2k-font: "Tahoma", "Verdana", sans-serif; + --y2k-bg: rgba(0, 0, 0, 0.18); + --y2k-surface: #f3f3f3; + --y2k-border-light: #ffffff; + --y2k-border-dark: #5a5a5a; + --y2k-title-start: #1d4f9a; + --y2k-title-end: #3b7ad0; + --y2k-title-text: #ffffff; + --y2k-text: #111111; + --y2k-button-bg: #e3e3e3; + --y2k-button-text: #111111; + --y2k-button-border-light: #ffffff; + --y2k-button-border-dark: #6d6d6d; + --y2k-focus: #0a6cff; + --y2k-type-info: #2f5ca8; + --y2k-type-warning: #d08900; + --y2k-type-error: #b62828; + --y2k-type-success: #288c39; +} + +.y2k-alert-overlay { + position: fixed; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + padding: 16px; + background: var(--y2k-bg); + z-index: 9999; +} + + +.y2k-alert { + width: min(92vw, 360px); + color: var(--y2k-text); + background: var(--y2k-surface); + border-style: solid; + border-width: 2px; + border-color: var(--y2k-border-light) var(--y2k-border-dark) var(--y2k-border-dark) var(--y2k-border-light); + box-shadow: 4px 4px 0 rgba(0, 0, 0, 0.22); + font-family: var(--y2k-font); +} + +.y2k-alert__titlebar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + padding: 8px; + color: var(--y2k-title-text); + background: linear-gradient(90deg, var(--y2k-title-start), var(--y2k-title-end)); +} + +.y2k-alert__title { + margin: 0; + font-size: 14px; + font-weight: 700; + line-height: 1; +} + +.y2k-alert__content { + padding: 12px; +} + +.y2k-alert__text { + margin: 0; + font-size: 14px; + line-height: 1.4; +} + +.y2k-alert__input { + width: 100%; + margin-top: 12px; + box-sizing: border-box; + border: 1px solid var(--y2k-border-dark); + padding: 6px 8px; + font-family: var(--y2k-font); + font-size: 14px; +} + +.y2k-alert__actions { + display: flex; + justify-content: flex-end; + gap: 8px; + margin-top: 12px; +} + +.y2k-alert--info { + border-left: 4px solid var(--y2k-type-info); +} + +.y2k-alert--warning { + border-left: 4px solid var(--y2k-type-warning); +} + +.y2k-alert--error { + border-left: 4px solid var(--y2k-type-error); +} + +.y2k-alert--success { + border-left: 4px solid var(--y2k-type-success); +} + + +.y2k-btn { + border-style: solid; + border-width: 2px; + border-color: var(--y2k-button-border-light) var(--y2k-button-border-dark) var(--y2k-button-border-dark) var(--y2k-button-border-light); + background: var(--y2k-button-bg); + color: var(--y2k-button-text); + padding: 4px 12px; + font-family: var(--y2k-font); + font-size: 13px; + cursor: pointer; +} + +.y2k-btn:active { + border-color: var(--y2k-button-border-dark) var(--y2k-button-border-light) var(--y2k-button-border-light) var(--y2k-button-border-dark); +} + +.y2k-btn:focus-visible, +.y2k-alert__input:focus-visible { + outline: 2px solid var(--y2k-focus); + outline-offset: 1px; +} + +.y2k-btn--close { + min-width: 28px; + padding: 2px 8px; + line-height: 1; +} + + +:root { + --y2k-title-start: #4457d9; + --y2k-title-end: #1a2d8f; + --y2k-bg: rgba(20, 20, 20, 0.22); +} diff --git a/dist/y2k-alerts.js b/dist/y2k-alerts.js new file mode 100644 index 0000000..5d3a968 --- /dev/null +++ b/dist/y2k-alerts.js @@ -0,0 +1,229 @@ +// CONSTANTS +((global) => { + + global.Y2K = { + TYPE_INFO: "info", + TYPE_WARNING: "warning", + TYPE_ERROR: "error", + TYPE_SUCCESS: "success" + }; + +})(window); + + +((global) => { + + function createElement(tagName, attributes, text) { + var element = document.createElement(tagName); + var attrs = attributes || {}; + var keys = Object.keys(attrs); + + for (var i = 0; i < keys.length; i += 1) { + element.setAttribute(keys[i], attrs[keys[i]]); + } + + if (text !== undefined && text !== null) { + element.textContent = text; + } + + return element; + } + + function appendChildren(parent, children) { + for (var i = 0; i < children.length; i += 1) { + parent.appendChild(children[i]); + } + } + + global.Y2KDom = { + createElement: createElement, + appendChildren: appendChildren + }; + +})(window); + + +((global) => { + + var Y2K = global.Y2K; + var createElement = global.Y2KDom.createElement; + var appendChildren = global.Y2KDom.appendChildren; + + class y2kAlert extends EventTarget { + constructor(title, type, message, options) { + super(); + this.title = title || "Alert"; + this.type = type || Y2K.TYPE_INFO; + this.message = message || ""; + this.options = options || {}; + this.isVisible = false; + this.inputElement = null; + this.overlayElement = null; + this.dialogElement = null; + this.submitButton = null; + this.closeButton = null; + + this._build(); + this._bindEvents(); + } + + _build() { + this.overlayElement = createElement("div", { + class: "y2k-alert-overlay" + }); + + this.dialogElement = createElement("section", { + class: "y2k-alert y2k-alert--" + this.type, + role: "dialog", + "aria-modal": "true", + "aria-label": this.title + }); + + var titleBar = createElement("header", { + class: "y2k-alert__titlebar" + }); + + var titleElement = createElement("h2", { + class: "y2k-alert__title" + }, this.title); + + this.closeButton = createElement("button", { + class: "y2k-btn y2k-btn--close", + type: "button", + "aria-label": "Close alert" + }, "x"); + + appendChildren(titleBar, [titleElement, this.closeButton]); + + var content = createElement("div", { + class: "y2k-alert__content" + }); + + var textElement = createElement("p", { + class: "y2k-alert__text" + }, this.message); + + content.appendChild(textElement); + + if (this.options.input) { + var inputConfig = typeof this.options.input === "object" ? this.options.input : {}; + this.inputElement = createElement("input", { + class: "y2k-alert__input", + type: inputConfig.type || "text", + placeholder: inputConfig.placeholder || "" + }); + + if (inputConfig.value) { + this.inputElement.setAttribute("value", inputConfig.value); + } + + content.appendChild(this.inputElement); + } + + var actions = createElement("footer", { + class: "y2k-alert__actions" + }); + + this.submitButton = createElement("button", { + class: "y2k-btn", + type: "button" + }, this.options.submitText || "OK"); + + actions.appendChild(this.submitButton); + appendChildren(content, [actions]); + appendChildren(this.dialogElement, [titleBar, content]); + this.overlayElement.appendChild(this.dialogElement); + } + + _bindEvents() { + var self = this; + + this.closeButton.addEventListener("click", function () { + self.close("close-button"); + }); + + this.submitButton.addEventListener("click", function () { + self.submit(); + }); + + if (this.inputElement) { + this.inputElement.addEventListener("keydown", function (event) { + if (event.key === "Enter") { + self.submit(); + } + }); + } + } + + show(parent) { + var mountPoint = parent || document.body; + if (!this.isVisible) { + mountPoint.appendChild(this.overlayElement); + this.isVisible = true; + } + + if (this.inputElement) { + this.inputElement.focus(); + } else { + this.submitButton.focus(); + } + + return this; + } + + hide() { + if (this.isVisible && this.overlayElement.parentNode) { + this.overlayElement.parentNode.removeChild(this.overlayElement); + } + + this.isVisible = false; + return this; + } + + close(reason) { + this.hide(); + this.dispatchEvent(new CustomEvent("close", { + detail: { + reason: reason || "close" + } + })); + return this; + } + + submit() { + var value = this.inputElement ? this.inputElement.value : null; + this.dispatchEvent(new CustomEvent("submit", { + detail: { + value: value, + title: this.title, + message: this.message, + type: this.type + } + })); + this.hide(); + return this; + } + + static quick(title, type, message, options) { + var alertInstance = new y2kAlert(title, type, message, options); + alertInstance.show(); + return alertInstance; + } + } + + global.y2kAlert = y2kAlert; + +})(window); + + +((global) => { + + global.Y2KAlerts = { + y2kAlert: global.y2kAlert, + Y2K: global.Y2K, + quick: function (title, type, message, options) { + return global.y2kAlert.quick(title, type, message, options); + } + }; + +})(window); diff --git a/dist/y2k-alerts.min.css b/dist/y2k-alerts.min.css new file mode 100644 index 0000000..9d652c5 --- /dev/null +++ b/dist/y2k-alerts.min.css @@ -0,0 +1 @@ +:root{--y2k-font:"Tahoma","Verdana",sans-serif;--y2k-bg:rgba(0,0,0,0.18);--y2k-surface:#f3f3f3;--y2k-border-light:#ffffff;--y2k-border-dark:#5a5a5a;--y2k-title-start:#1d4f9a;--y2k-title-end:#3b7ad0;--y2k-title-text:#ffffff;--y2k-text:#111111;--y2k-button-bg:#e3e3e3;--y2k-button-text:#111111;--y2k-button-border-light:#ffffff;--y2k-button-border-dark:#6d6d6d;--y2k-focus:#0a6cff;--y2k-type-info:#2f5ca8;--y2k-type-warning:#d08900;--y2k-type-error:#b62828;--y2k-type-success:#288c39}.y2k-alert-overlay{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;padding:16px;background:var(--y2k-bg);z-index:9999}.y2k-alert{width:min(92vw,360px);color:var(--y2k-text);background:var(--y2k-surface);border-style:solid;border-width:2px;border-color:var(--y2k-border-light) var(--y2k-border-dark) var(--y2k-border-dark) var(--y2k-border-light);box-shadow:4px 4px 0 rgba(0,0,0,0.22);font-family:var(--y2k-font)}.y2k-alert__titlebar{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px;color:var(--y2k-title-text);background:linear-gradient(90deg,var(--y2k-title-start),var(--y2k-title-end))}.y2k-alert__title{margin:0;font-size:14px;font-weight:700;line-height:1}.y2k-alert__content{padding:12px}.y2k-alert__text{margin:0;font-size:14px;line-height:1.4}.y2k-alert__input{width:100%;margin-top:12px;box-sizing:border-box;border:1px solid var(--y2k-border-dark);padding:6px 8px;font-family:var(--y2k-font);font-size:14px}.y2k-alert__actions{display:flex;justify-content:flex-end;gap:8px;margin-top:12px}.y2k-alert--info{border-left:4px solid var(--y2k-type-info)}.y2k-alert--warning{border-left:4px solid var(--y2k-type-warning)}.y2k-alert--error{border-left:4px solid var(--y2k-type-error)}.y2k-alert--success{border-left:4px solid var(--y2k-type-success)}.y2k-btn{border-style:solid;border-width:2px;border-color:var(--y2k-button-border-light) var(--y2k-button-border-dark) var(--y2k-button-border-dark) var(--y2k-button-border-light);background:var(--y2k-button-bg);color:var(--y2k-button-text);padding:4px 12px;font-family:var(--y2k-font);font-size:13px;cursor:pointer}.y2k-btn:active{border-color:var(--y2k-button-border-dark) var(--y2k-button-border-light) var(--y2k-button-border-light) var(--y2k-button-border-dark)}.y2k-btn:focus-visible,.y2k-alert__input:focus-visible{outline:2px solid var(--y2k-focus);outline-offset:1px}.y2k-btn--close{min-width:28px;padding:2px 8px;line-height:1}:root{--y2k-title-start:#4457d9;--y2k-title-end:#1a2d8f;--y2k-bg:rgba(20,20,20,0.22)} \ No newline at end of file diff --git a/dist/y2k-alerts.min.js b/dist/y2k-alerts.min.js new file mode 100644 index 0000000..76b4a23 --- /dev/null +++ b/dist/y2k-alerts.min.js @@ -0,0 +1 @@ +((global) => { global.Y2K = { TYPE_INFO: "info", TYPE_WARNING: "warning", TYPE_ERROR: "error", TYPE_SUCCESS: "success" }; })(window); ((global) => { function createElement(tagName, attributes, text) { var element = document.createElement(tagName); var attrs = attributes || {}; var keys = Object.keys(attrs); for (var i = 0; i < keys.length; i += 1) { element.setAttribute(keys[i], attrs[keys[i]]); } if (text !== undefined && text !== null) { element.textContent = text; } return element; } function appendChildren(parent, children) { for (var i = 0; i < children.length; i += 1) { parent.appendChild(children[i]); } } global.Y2KDom = { createElement: createElement, appendChildren: appendChildren }; })(window); ((global) => { var Y2K = global.Y2K; var createElement = global.Y2KDom.createElement; var appendChildren = global.Y2KDom.appendChildren; class y2kAlert extends EventTarget { constructor(title, type, message, options) { super(); this.title = title || "Alert"; this.type = type || Y2K.TYPE_INFO; this.message = message || ""; this.options = options || {}; this.isVisible = false; this.inputElement = null; this.overlayElement = null; this.dialogElement = null; this.submitButton = null; this.closeButton = null; this._build(); this._bindEvents(); } _build() { this.overlayElement = createElement("div", { class: "y2k-alert-overlay" }); this.dialogElement = createElement("section", { class: "y2k-alert y2k-alert--" + this.type, role: "dialog", "aria-modal": "true", "aria-label": this.title }); var titleBar = createElement("header", { class: "y2k-alert__titlebar" }); var titleElement = createElement("h2", { class: "y2k-alert__title" }, this.title); this.closeButton = createElement("button", { class: "y2k-btn y2k-btn--close", type: "button", "aria-label": "Close alert" }, "x"); appendChildren(titleBar, [titleElement, this.closeButton]); var content = createElement("div", { class: "y2k-alert__content" }); var textElement = createElement("p", { class: "y2k-alert__text" }, this.message); content.appendChild(textElement); if (this.options.input) { var inputConfig = typeof this.options.input === "object" ? this.options.input : {}; this.inputElement = createElement("input", { class: "y2k-alert__input", type: inputConfig.type || "text", placeholder: inputConfig.placeholder || "" }); if (inputConfig.value) { this.inputElement.setAttribute("value", inputConfig.value); } content.appendChild(this.inputElement); } var actions = createElement("footer", { class: "y2k-alert__actions" }); this.submitButton = createElement("button", { class: "y2k-btn", type: "button" }, this.options.submitText || "OK"); actions.appendChild(this.submitButton); appendChildren(content, [actions]); appendChildren(this.dialogElement, [titleBar, content]); this.overlayElement.appendChild(this.dialogElement); } _bindEvents() { var self = this; this.closeButton.addEventListener("click", function () { self.close("close-button"); }); this.submitButton.addEventListener("click", function () { self.submit(); }); if (this.inputElement) { this.inputElement.addEventListener("keydown", function (event) { if (event.key === "Enter") { self.submit(); } }); } } show(parent) { var mountPoint = parent || document.body; if (!this.isVisible) { mountPoint.appendChild(this.overlayElement); this.isVisible = true; } if (this.inputElement) { this.inputElement.focus(); } else { this.submitButton.focus(); } return this; } hide() { if (this.isVisible && this.overlayElement.parentNode) { this.overlayElement.parentNode.removeChild(this.overlayElement); } this.isVisible = false; return this; } close(reason) { this.hide(); this.dispatchEvent(new CustomEvent("close", { detail: { reason: reason || "close" } })); return this; } submit() { var value = this.inputElement ? this.inputElement.value : null; this.dispatchEvent(new CustomEvent("submit", { detail: { value: value, title: this.title, message: this.message, type: this.type } })); this.hide(); return this; } static quick(title, type, message, options) { var alertInstance = new y2kAlert(title, type, message, options); alertInstance.show(); return alertInstance; } } global.y2kAlert = y2kAlert; })(window); ((global) => { global.Y2KAlerts = { y2kAlert: global.y2kAlert, Y2K: global.Y2K, quick: function (title, type, message, options) { return global.y2kAlert.quick(title, type, message, options); } }; })(window); \ No newline at end of file diff --git a/examples/generic.html b/examples/generic.html new file mode 100644 index 0000000..1a27404 --- /dev/null +++ b/examples/generic.html @@ -0,0 +1,71 @@ + + + + + + Y2K Alerts - Generic + + + + +

Generic Theme

+
+ + +
+ + + + + diff --git a/examples/win98.html b/examples/win98.html new file mode 100644 index 0000000..826e3f1 --- /dev/null +++ b/examples/win98.html @@ -0,0 +1,30 @@ + + + + + + Y2K Alerts - Win98 Theme + + + + + + + + + + diff --git a/examples/winxp.html b/examples/winxp.html new file mode 100644 index 0000000..e758dba --- /dev/null +++ b/examples/winxp.html @@ -0,0 +1,41 @@ + + + + + + Y2K Alerts - WinXP Theme + + + + + + + + + + diff --git a/js/constants.js b/js/constants.js new file mode 100644 index 0000000..d52225e --- /dev/null +++ b/js/constants.js @@ -0,0 +1,11 @@ +// CONSTANTS +((global) => { + + global.Y2K = { + TYPE_INFO: "info", + TYPE_WARNING: "warning", + TYPE_ERROR: "error", + TYPE_SUCCESS: "success" + }; + +})(window); diff --git a/js/dom.js b/js/dom.js new file mode 100644 index 0000000..a24e26c --- /dev/null +++ b/js/dom.js @@ -0,0 +1,30 @@ +((global) => { + + function createElement(tagName, attributes, text) { + var element = document.createElement(tagName); + var attrs = attributes || {}; + var keys = Object.keys(attrs); + + for (var i = 0; i < keys.length; i += 1) { + element.setAttribute(keys[i], attrs[keys[i]]); + } + + if (text !== undefined && text !== null) { + element.textContent = text; + } + + return element; + } + + function appendChildren(parent, children) { + for (var i = 0; i < children.length; i += 1) { + parent.appendChild(children[i]); + } + } + + global.Y2KDom = { + createElement: createElement, + appendChildren: appendChildren + }; + +})(window); diff --git a/js/index.js b/js/index.js new file mode 100644 index 0000000..7d7e778 --- /dev/null +++ b/js/index.js @@ -0,0 +1,11 @@ +((global) => { + + global.Y2KAlerts = { + y2kAlert: global.y2kAlert, + Y2K: global.Y2K, + quick: function (title, type, message, options) { + return global.y2kAlert.quick(title, type, message, options); + } + }; + +})(window); diff --git a/js/y2k-alert.js b/js/y2k-alert.js new file mode 100644 index 0000000..97782cc --- /dev/null +++ b/js/y2k-alert.js @@ -0,0 +1,171 @@ +((global) => { + + var Y2K = global.Y2K; + var createElement = global.Y2KDom.createElement; + var appendChildren = global.Y2KDom.appendChildren; + + class y2kAlert extends EventTarget { + constructor(title, type, message, options) { + super(); + this.title = title || "Alert"; + this.type = type || Y2K.TYPE_INFO; + this.message = message || ""; + this.options = options || {}; + this.isVisible = false; + this.inputElement = null; + this.overlayElement = null; + this.dialogElement = null; + this.submitButton = null; + this.closeButton = null; + + this._build(); + this._bindEvents(); + } + + _build() { + this.overlayElement = createElement("div", { + class: "y2k-alert-overlay" + }); + + this.dialogElement = createElement("section", { + class: "y2k-alert y2k-alert--" + this.type, + role: "dialog", + "aria-modal": "true", + "aria-label": this.title + }); + + var titleBar = createElement("header", { + class: "y2k-alert__titlebar" + }); + + var titleElement = createElement("h2", { + class: "y2k-alert__title" + }, this.title); + + this.closeButton = createElement("button", { + class: "y2k-btn y2k-btn--close", + type: "button", + "aria-label": "Close alert" + }, "x"); + + appendChildren(titleBar, [titleElement, this.closeButton]); + + var content = createElement("div", { + class: "y2k-alert__content" + }); + + var textElement = createElement("p", { + class: "y2k-alert__text" + }, this.message); + + content.appendChild(textElement); + + if (this.options.input) { + var inputConfig = typeof this.options.input === "object" ? this.options.input : {}; + this.inputElement = createElement("input", { + class: "y2k-alert__input", + type: inputConfig.type || "text", + placeholder: inputConfig.placeholder || "" + }); + + if (inputConfig.value) { + this.inputElement.setAttribute("value", inputConfig.value); + } + + content.appendChild(this.inputElement); + } + + var actions = createElement("footer", { + class: "y2k-alert__actions" + }); + + this.submitButton = createElement("button", { + class: "y2k-btn", + type: "button" + }, this.options.submitText || "OK"); + + actions.appendChild(this.submitButton); + appendChildren(content, [actions]); + appendChildren(this.dialogElement, [titleBar, content]); + this.overlayElement.appendChild(this.dialogElement); + } + + _bindEvents() { + var self = this; + + this.closeButton.addEventListener("click", function () { + self.close("close-button"); + }); + + this.submitButton.addEventListener("click", function () { + self.submit(); + }); + + if (this.inputElement) { + this.inputElement.addEventListener("keydown", function (event) { + if (event.key === "Enter") { + self.submit(); + } + }); + } + } + + show(parent) { + var mountPoint = parent || document.body; + if (!this.isVisible) { + mountPoint.appendChild(this.overlayElement); + this.isVisible = true; + } + + if (this.inputElement) { + this.inputElement.focus(); + } else { + this.submitButton.focus(); + } + + return this; + } + + hide() { + if (this.isVisible && this.overlayElement.parentNode) { + this.overlayElement.parentNode.removeChild(this.overlayElement); + } + + this.isVisible = false; + return this; + } + + close(reason) { + this.hide(); + this.dispatchEvent(new CustomEvent("close", { + detail: { + reason: reason || "close" + } + })); + return this; + } + + submit() { + var value = this.inputElement ? this.inputElement.value : null; + this.dispatchEvent(new CustomEvent("submit", { + detail: { + value: value, + title: this.title, + message: this.message, + type: this.type + } + })); + this.hide(); + return this; + } + + static quick(title, type, message, options) { + var alertInstance = new y2kAlert(title, type, message, options); + alertInstance.show(); + return alertInstance; + } + } + + global.y2kAlert = y2kAlert; + +})(window); diff --git a/package.json b/package.json index 0764dc7..a339048 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,10 @@ "license": "Apache-2.0", "author": "Daniel Legt", "type": "commonjs", - "main": "index.js", + "main": "dist/y2k-alerts.js", + "style": "dist/y2k-alerts.css", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "build": "node scripts/build.js", + "test": "npm run build" } } diff --git a/scripts/build.js b/scripts/build.js new file mode 100644 index 0000000..5ad8d9d --- /dev/null +++ b/scripts/build.js @@ -0,0 +1,93 @@ +const fs = require("fs"); +const path = require("path"); + +const ROOT_DIR = path.resolve(__dirname, ".."); +const DIST_DIR = path.join(ROOT_DIR, "dist"); +const BASE_CSS_DIR = path.join(ROOT_DIR, "css", "base"); +const THEMES_DIR = path.join(ROOT_DIR, "css", "themes"); +const JS_DIR = path.join(ROOT_DIR, "js"); + +const BASE_CSS_FILES = ["base.css", "alert.css", "buttons.css"]; +const JS_FILES = ["constants.js", "dom.js", "y2k-alert.js", "index.js"]; + +function ensureDir(dirPath) { + fs.mkdirSync(dirPath, { recursive: true }); +} + +function readFiles(folder, files) { + return files + .map((file) => fs.readFileSync(path.join(folder, file), "utf8")) + .join("\n\n"); +} + +function minifyCss(css) { + return css + .replace(/\/\*[\s\S]*?\*\//g, "") + .replace(/\s+/g, " ") + .replace(/\s*([{}:;,])\s*/g, "$1") + .replace(/;}/g, "}") + .trim(); +} + +function minifyJs(js) { + return js + .replace(/\/\*[\s\S]*?\*\//g, "") + .replace(/^\s*\/\/.*$/gm, "") + .replace(/\s+/g, " ") + .trim(); +} + +function buildCss() { + const baseCss = readFiles(BASE_CSS_DIR, BASE_CSS_FILES); + const themeFolders = fs + .readdirSync(THEMES_DIR, { withFileTypes: true }) + .filter((entry) => entry.isDirectory()) + .map((entry) => entry.name) + .sort(); + + themeFolders.forEach((themeName) => { + const themePath = path.join(THEMES_DIR, themeName); + const themeFiles = fs + .readdirSync(themePath) + .filter((file) => file.endsWith(".css")) + .sort(); + + const themeCss = readFiles(themePath, themeFiles); + const output = `${baseCss}\n\n${themeCss}`; + + const distFile = path.join(DIST_DIR, `y2k-alerts-${themeName}.css`); + const distMinFile = path.join(DIST_DIR, `y2k-alerts-${themeName}.min.css`); + + fs.writeFileSync(distFile, output, "utf8"); + fs.writeFileSync(distMinFile, minifyCss(output), "utf8"); + }); + + const genericFile = path.join(DIST_DIR, "y2k-alerts-generic.css"); + const genericMinFile = path.join(DIST_DIR, "y2k-alerts-generic.min.css"); + + if (fs.existsSync(genericFile)) { + fs.copyFileSync(genericFile, path.join(DIST_DIR, "y2k-alerts.css")); + } + + if (fs.existsSync(genericMinFile)) { + fs.copyFileSync(genericMinFile, path.join(DIST_DIR, "y2k-alerts.min.css")); + } +} + +function buildJs() { + const jsBundle = readFiles(JS_DIR, JS_FILES); + const distFile = path.join(DIST_DIR, "y2k-alerts.js"); + const distMinFile = path.join(DIST_DIR, "y2k-alerts.min.js"); + + fs.writeFileSync(distFile, jsBundle, "utf8"); + fs.writeFileSync(distMinFile, minifyJs(jsBundle), "utf8"); +} + +function build() { + ensureDir(DIST_DIR); + buildCss(); + buildJs(); + console.log("Built y2k-alerts assets in dist/"); +} + +build();