web: start: move css to dedicated file
Signed-off-by: GnomeZworc <nicolas.boufidjeline@g3e.fr>
This commit is contained in:
parent
158dd0451a
commit
6e976ac6e3
10 changed files with 288 additions and 309 deletions
38
web/components/date-display/date-display.css
Normal file
38
web/components/date-display/date-display.css
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
background: #1e1e2e;
|
||||||
|
border: 1px solid #313244;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px 20px;
|
||||||
|
font-family: monospace;
|
||||||
|
color: #cdd6f4;
|
||||||
|
min-width: 260px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-size: 11px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
color: #6c7086;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #89b4fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #a6adc8;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent {
|
||||||
|
margin-top: 12px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #585b70;
|
||||||
|
border-top: 1px solid #313244;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
@ -1,53 +1,19 @@
|
||||||
import { config } from '../../core/config.js'
|
import { config } from '../../core/config.js'
|
||||||
|
|
||||||
|
const CSS = new URL('./date-display.css', import.meta.url).href
|
||||||
|
|
||||||
class DateDisplay extends HTMLElement {
|
class DateDisplay extends HTMLElement {
|
||||||
#interval = null
|
#interval = null
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
this.attachShadow({ mode: 'open' })
|
this.attachShadow({ mode: 'open' })
|
||||||
this.shadowRoot.innerHTML = `
|
this.shadowRoot.innerHTML = `
|
||||||
<style>
|
<link rel="stylesheet" href="${CSS}">
|
||||||
:host {
|
|
||||||
display: block;
|
|
||||||
background: #1e1e2e;
|
|
||||||
border: 1px solid #313244;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 16px 20px;
|
|
||||||
font-family: monospace;
|
|
||||||
color: #cdd6f4;
|
|
||||||
min-width: 260px;
|
|
||||||
}
|
|
||||||
.label {
|
|
||||||
font-size: 11px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.08em;
|
|
||||||
color: #6c7086;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
}
|
|
||||||
.time {
|
|
||||||
font-size: 28px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #89b4fa;
|
|
||||||
}
|
|
||||||
.date {
|
|
||||||
font-size: 13px;
|
|
||||||
color: #a6adc8;
|
|
||||||
margin-top: 4px;
|
|
||||||
}
|
|
||||||
.agent {
|
|
||||||
margin-top: 12px;
|
|
||||||
font-size: 11px;
|
|
||||||
color: #585b70;
|
|
||||||
border-top: 1px solid #313244;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<div class="label">Current time</div>
|
<div class="label">Current time</div>
|
||||||
<div class="time" id="time"></div>
|
<div class="time" id="time"></div>
|
||||||
<div class="date" id="date"></div>
|
<div class="date" id="date"></div>
|
||||||
<div class="agent">agent → <span id="agent-url"></span></div>
|
<div class="agent">agent → <span id="agent-url"></span></div>
|
||||||
`
|
`
|
||||||
|
|
||||||
this.shadowRoot.getElementById('agent-url').textContent = config.agent_url
|
this.shadowRoot.getElementById('agent-url').textContent = config.agent_url
|
||||||
this.#tick()
|
this.#tick()
|
||||||
this.#interval = setInterval(() => this.#tick(), 1000)
|
this.#interval = setInterval(() => this.#tick(), 1000)
|
||||||
|
|
|
||||||
81
web/components/login-gate/login-gate.css
Normal file
81
web/components/login-gate/login-gate.css
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
|
||||||
|
.overlay {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: #181825;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 9999;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: #1e1e2e;
|
||||||
|
border: 1px solid #313244;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 36px 40px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 420px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.card { padding: 24px 20px; margin: 0 12px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.header { display: flex; flex-direction: column; gap: 6px; }
|
||||||
|
|
||||||
|
.header h1 {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #89b4fa;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header p { font-size: 13px; color: #6c7086; }
|
||||||
|
|
||||||
|
.fields { display: flex; flex-direction: column; gap: 14px; }
|
||||||
|
.field { display: flex; flex-direction: column; gap: 6px; }
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #a6adc8;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
background: #181825;
|
||||||
|
border: 1px solid #313244;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 9px 12px;
|
||||||
|
color: #cdd6f4;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: monospace;
|
||||||
|
outline: none;
|
||||||
|
transition: border-color 0.15s;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus { border-color: #89b4fa; }
|
||||||
|
input::placeholder { color: #45475a; }
|
||||||
|
|
||||||
|
button {
|
||||||
|
background: #89b4fa;
|
||||||
|
color: #1e1e2e;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 10px 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.15s;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover { background: #b4d0fa; }
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
// Phase 2: swap for <oidc-gate> that resolves ready with a JWT — api-client unchanged.
|
// Phase 2: swap for <oidc-gate> that resolves ready with a JWT — api-client unchanged.
|
||||||
|
|
||||||
const SESSION_KEY = 'two:credentials'
|
const SESSION_KEY = 'two:credentials'
|
||||||
|
const CSS = new URL('./login-gate.css', import.meta.url).href
|
||||||
|
|
||||||
class LoginGate extends HTMLElement {
|
class LoginGate extends HTMLElement {
|
||||||
#resolve = null
|
#resolve = null
|
||||||
|
|
@ -57,107 +58,7 @@ class LoginGate extends HTMLElement {
|
||||||
const overlay = document.createElement('div')
|
const overlay = document.createElement('div')
|
||||||
overlay.attachShadow({ mode: 'open' })
|
overlay.attachShadow({ mode: 'open' })
|
||||||
overlay.shadowRoot.innerHTML = `
|
overlay.shadowRoot.innerHTML = `
|
||||||
<style>
|
<link rel="stylesheet" href="${CSS}">
|
||||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
||||||
|
|
||||||
.overlay {
|
|
||||||
position: fixed;
|
|
||||||
inset: 0;
|
|
||||||
background: #181825;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
z-index: 9999;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
background: #1e1e2e;
|
|
||||||
border: 1px solid #313244;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 36px 40px;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 420px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header h1 {
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #89b4fa;
|
|
||||||
letter-spacing: 0.02em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header p {
|
|
||||||
font-size: 13px;
|
|
||||||
color: #6c7086;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fields {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.field {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #a6adc8;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.06em;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
background: #181825;
|
|
||||||
border: 1px solid #313244;
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 9px 12px;
|
|
||||||
color: #cdd6f4;
|
|
||||||
font-size: 14px;
|
|
||||||
font-family: monospace;
|
|
||||||
outline: none;
|
|
||||||
transition: border-color 0.15s;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:focus {
|
|
||||||
border-color: #89b4fa;
|
|
||||||
}
|
|
||||||
|
|
||||||
input::placeholder {
|
|
||||||
color: #45475a;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
background: #89b4fa;
|
|
||||||
color: #1e1e2e;
|
|
||||||
border: none;
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 10px 16px;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.15s, opacity 0.15s;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover { background: #b4d0fa; }
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="overlay">
|
<div class="overlay">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
|
|
|
||||||
30
web/components/logout-button/logout-button.css
Normal file
30
web/components/logout-button/logout-button.css
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
:host {
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 7px;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid #313244;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 7px 12px;
|
||||||
|
color: #a6adc8;
|
||||||
|
font-size: 13px;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.12s, color 0.12s, border-color 0.12s;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: #313244;
|
||||||
|
color: #f38ba8;
|
||||||
|
border-color: #f38ba8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon { display: flex; align-items: center; }
|
||||||
|
|
||||||
|
/* compact attribute — icon only */
|
||||||
|
:host([compact]) button { padding: 7px; }
|
||||||
|
:host([compact]) .label { display: none; }
|
||||||
|
|
@ -1,63 +1,21 @@
|
||||||
// Logout button — drop anywhere in the DOM.
|
const CSS = new URL('./logout-button.css', import.meta.url).href
|
||||||
//
|
|
||||||
// <logout-button></logout-button> full button with label
|
|
||||||
// <logout-button compact></logout-button> icon only (for tight spaces)
|
|
||||||
//
|
|
||||||
// Delegates to <login-gate>.logout(). Works regardless of where it sits
|
|
||||||
// (header, members panel, dropdown…) since it resolves the gate from the document.
|
|
||||||
//
|
|
||||||
// Phase 2: if login-gate is swapped for oidc-gate, this still works as long as
|
|
||||||
// the auth component exposes a logout() method (see #gate()).
|
|
||||||
|
|
||||||
const ICON = `<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>`
|
const ICON = `<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>`
|
||||||
|
|
||||||
class LogoutButton extends HTMLElement {
|
class LogoutButton extends HTMLElement {
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
this.attachShadow({ mode: 'open' })
|
this.attachShadow({ mode: 'open' })
|
||||||
|
|
||||||
const compact = this.hasAttribute('compact')
|
|
||||||
|
|
||||||
this.shadowRoot.innerHTML = `
|
this.shadowRoot.innerHTML = `
|
||||||
<style>
|
<link rel="stylesheet" href="${CSS}">
|
||||||
:host { display: inline-flex; }
|
|
||||||
|
|
||||||
button {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 7px;
|
|
||||||
background: transparent;
|
|
||||||
border: 1px solid #313244;
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: ${compact ? '7px' : '7px 12px'};
|
|
||||||
color: #a6adc8;
|
|
||||||
font-size: 13px;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.12s, color 0.12s, border-color 0.12s;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background: #313244;
|
|
||||||
color: #f38ba8;
|
|
||||||
border-color: #f38ba8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon { display: flex; align-items: center; }
|
|
||||||
.label { ${compact ? 'display: none;' : ''} }
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<button type="button" title="Log out">
|
<button type="button" title="Log out">
|
||||||
<span class="icon">${ICON}</span>
|
<span class="icon">${ICON}</span>
|
||||||
<span class="label">Log out</span>
|
<span class="label">Log out</span>
|
||||||
</button>
|
</button>
|
||||||
`
|
`
|
||||||
|
|
||||||
this.shadowRoot.querySelector('button')
|
this.shadowRoot.querySelector('button')
|
||||||
.addEventListener('click', () => this.#logout())
|
.addEventListener('click', () => this.#logout())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolves the auth component. Today: login-gate. Tomorrow: any element
|
|
||||||
// exposing logout() (oidc-gate, oauth-gate…).
|
|
||||||
#gate() {
|
#gate() {
|
||||||
return document.querySelector('login-gate, [data-auth-gate]')
|
return document.querySelector('login-gate, [data-auth-gate]')
|
||||||
}
|
}
|
||||||
|
|
|
||||||
40
web/components/side-nav/side-nav.css
Normal file
40
web/components/side-nav/side-nav.css
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 220px;
|
||||||
|
min-width: 220px;
|
||||||
|
background: #1e1e2e;
|
||||||
|
border-right: 1px solid #313244;
|
||||||
|
padding: 16px 12px;
|
||||||
|
gap: 4px;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 11px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #a6adc8;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||||
|
min-height: 44px;
|
||||||
|
transition: background 0.1s, color 0.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item:hover { background: #313244; color: #cdd6f4; }
|
||||||
|
.item.active { background: #313244; color: #89b4fa; }
|
||||||
|
.item.active .icon { color: #89b4fa; }
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: #6c7086;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item:hover .icon { color: #cdd6f4; }
|
||||||
|
|
@ -18,7 +18,8 @@ const ICONS = {
|
||||||
vm: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg>`,
|
vm: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg>`,
|
||||||
}
|
}
|
||||||
|
|
||||||
const MQ = window.matchMedia('(max-width: 768px)')
|
const CSS = new URL('./side-nav.css', import.meta.url).href
|
||||||
|
const MQ = window.matchMedia('(max-width: 768px)')
|
||||||
const NAV_W = 220 // px
|
const NAV_W = 220 // px
|
||||||
|
|
||||||
function resolveIcon(name) {
|
function resolveIcon(name) {
|
||||||
|
|
@ -67,44 +68,7 @@ class SideNav extends HTMLElement {
|
||||||
}).join('')
|
}).join('')
|
||||||
|
|
||||||
this.shadowRoot.innerHTML = `
|
this.shadowRoot.innerHTML = `
|
||||||
<style>
|
<link rel="stylesheet" href="${CSS}">
|
||||||
:host {
|
|
||||||
/* Géométrie desktop définie ici ; mobile gérée par this.style en JS */
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: ${NAV_W}px;
|
|
||||||
min-width: ${NAV_W}px;
|
|
||||||
background: #1e1e2e;
|
|
||||||
border-right: 1px solid #313244;
|
|
||||||
padding: 16px 12px;
|
|
||||||
gap: 4px;
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden; /* empêche tout débordement */
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
padding: 11px 12px;
|
|
||||||
border-radius: 6px;
|
|
||||||
text-decoration: none;
|
|
||||||
color: #a6adc8;
|
|
||||||
font-size: 14px;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
||||||
min-height: 44px;
|
|
||||||
transition: background 0.1s, color 0.1s;
|
|
||||||
}
|
|
||||||
.item:hover { background: #313244; color: #cdd6f4; }
|
|
||||||
.item.active { background: #313244; color: #89b4fa; }
|
|
||||||
.item.active .icon { color: #89b4fa; }
|
|
||||||
.icon {
|
|
||||||
display: flex; align-items: center;
|
|
||||||
color: #6c7086; flex-shrink: 0;
|
|
||||||
}
|
|
||||||
.item:hover .icon { color: #cdd6f4; }
|
|
||||||
</style>
|
|
||||||
${links}
|
${links}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
|
||||||
83
web/components/side-panel/side-panel.css
Normal file
83
web/components/side-panel/side-panel.css
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
:host {
|
||||||
|
--width: 480px; /* overridden via this.style.setProperty in JS */
|
||||||
|
--offset: 0px;
|
||||||
|
--dur: 220ms;
|
||||||
|
--actual-width: min(var(--width), 100vw);
|
||||||
|
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: calc(-1 * var(--actual-width));
|
||||||
|
width: var(--actual-width);
|
||||||
|
height: 100vh;
|
||||||
|
background: #1e1e2e;
|
||||||
|
border-left: 1px solid #313244;
|
||||||
|
box-shadow: -8px 0 32px rgba(0, 0, 0, 0.4);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
z-index: calc(1000 + var(--stack, 0));
|
||||||
|
transition: right var(--dur) cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||||
|
color: #cdd6f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host([data-open]) {
|
||||||
|
right: var(--offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
:host {
|
||||||
|
--actual-width: 100vw;
|
||||||
|
}
|
||||||
|
:host([data-open]) {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-bottom: 1px solid #313244;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #cdd6f4;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 5px;
|
||||||
|
color: #6c7086;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.12s, color 0.12s, border-color 0.12s;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close:hover {
|
||||||
|
background: #313244;
|
||||||
|
border-color: #45475a;
|
||||||
|
color: #f38ba8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body::-webkit-scrollbar { width: 6px; }
|
||||||
|
.body::-webkit-scrollbar-track { background: transparent; }
|
||||||
|
.body::-webkit-scrollbar-thumb { background: #45475a; border-radius: 3px; }
|
||||||
|
|
@ -25,6 +25,8 @@
|
||||||
// Clicking the backdrop closes only the topmost panel.
|
// Clicking the backdrop closes only the topmost panel.
|
||||||
// ESC closes the topmost panel.
|
// ESC closes the topmost panel.
|
||||||
|
|
||||||
|
const CSS = new URL('./side-panel.css', import.meta.url).href
|
||||||
|
|
||||||
const CLOSE_ICON = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none"
|
const CLOSE_ICON = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none"
|
||||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
||||||
|
|
@ -103,95 +105,11 @@ export class SidePanel extends HTMLElement {
|
||||||
const title = this.getAttribute('title') ?? ''
|
const title = this.getAttribute('title') ?? ''
|
||||||
const width = this.getAttribute('width') ?? '480px'
|
const width = this.getAttribute('width') ?? '480px'
|
||||||
|
|
||||||
|
// Passe la largeur comme custom property — référencée dans le CSS via var(--width)
|
||||||
|
this.style.setProperty('--width', width)
|
||||||
|
|
||||||
this.shadowRoot.innerHTML = `
|
this.shadowRoot.innerHTML = `
|
||||||
<style>
|
<link rel="stylesheet" href="${CSS}">
|
||||||
:host {
|
|
||||||
--offset: 0px;
|
|
||||||
--width: ${width};
|
|
||||||
--dur: ${ANIM_DURATION}ms;
|
|
||||||
/* Cap width to viewport — never overflows on mobile */
|
|
||||||
--actual-width: min(var(--width), 100vw);
|
|
||||||
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
right: calc(-1 * var(--actual-width));
|
|
||||||
width: var(--actual-width);
|
|
||||||
height: 100vh;
|
|
||||||
background: #1e1e2e;
|
|
||||||
border-left: 1px solid #313244;
|
|
||||||
box-shadow: -8px 0 32px rgba(0,0,0,0.4);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
z-index: calc(1000 + var(--stack, 0));
|
|
||||||
transition: right var(--dur) cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
||||||
color: #cdd6f4;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([data-open]) {
|
|
||||||
right: var(--offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* On mobile: always full-width, no stack offset */
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
:host {
|
|
||||||
--actual-width: 100vw;
|
|
||||||
}
|
|
||||||
:host([data-open]) {
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
padding: 16px 20px;
|
|
||||||
border-bottom: 1px solid #313244;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
flex: 1;
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #cdd6f4;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.close {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
background: transparent;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 5px;
|
|
||||||
color: #6c7086;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.12s, color 0.12s, border-color 0.12s;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.close:hover {
|
|
||||||
background: #313244;
|
|
||||||
border-color: #45475a;
|
|
||||||
color: #f38ba8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.body {
|
|
||||||
flex: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.body::-webkit-scrollbar { width: 6px; }
|
|
||||||
.body::-webkit-scrollbar-track { background: transparent; }
|
|
||||||
.body::-webkit-scrollbar-thumb { background: #45475a; border-radius: 3px; }
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<span class="title">${title}</span>
|
<span class="title">${title}</span>
|
||||||
<button class="close" aria-label="Close">${CLOSE_ICON}</button>
|
<button class="close" aria-label="Close">${CLOSE_ICON}</button>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue