feat: calendrier impérial et conversions calendaires
- calendrier impérial : fenêtre compacte (260px), affichage date - commande /cal, auto-ouverture paramétrable dans les settings - socket calendarSync : mise à jour temps réel pour les joueurs - config MJ : année, jour (label live), heure:minute, planète - boutons +1 jour et +1 heure (report 24h→jour, 365→1) - conversions : Solomani, Vilani, Zhodani (Olympiade), Aslan, K'Kree, Hiver - formules wiki.travellerrpg.com/Date_Conversion, section repliable - fix: renderChatInput /cal trop large → exact match - fix: fenêtre refermable (onClose nullifie la référence) - fix: socket joueur lit data direct au lieu de readCalendar()
This commit is contained in:
+2
-1
@@ -21,7 +21,8 @@
|
||||
"scripts/TravellerNpcDialog.js",
|
||||
"scripts/mgt2eMigration.js",
|
||||
"scripts/npcRollTableSync.js",
|
||||
"scripts/mgt2eSkills.js"
|
||||
"scripts/mgt2eSkills.js",
|
||||
"scripts/imperialCalendar.js"
|
||||
],
|
||||
"styles": [
|
||||
"styles/commerce.css",
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import { formatImperialDate } from './calendarDate.js';
|
||||
|
||||
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
const MODULE_ID = 'mgt2-compendium-amiral-denisov';
|
||||
|
||||
export class CalendarApp extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
static DEFAULT_OPTIONS = {
|
||||
id: 'mgt2-calendar',
|
||||
classes: ['mgt2-calendar'],
|
||||
position: { width: 260, height: 'auto' },
|
||||
window: {
|
||||
icon: 'fas fa-calendar-alt',
|
||||
title: 'Calendrier Impérial',
|
||||
resizable: false,
|
||||
},
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: `modules/${MODULE_ID}/templates/calendar-app.hbs`,
|
||||
root: true,
|
||||
},
|
||||
};
|
||||
|
||||
constructor({ onConfig, onClose } = {}) {
|
||||
super();
|
||||
this._onConfig = onConfig;
|
||||
this._onClose = onClose;
|
||||
this._data = { year: 1116, day: 1, hour: 12, minute: 0, planet: '' };
|
||||
}
|
||||
|
||||
get title() {
|
||||
return 'Calendrier Impérial';
|
||||
}
|
||||
|
||||
async _prepareContext() {
|
||||
return {
|
||||
dateLabel: formatImperialDate(this._data),
|
||||
isGM: game.user?.isGM ?? false,
|
||||
};
|
||||
}
|
||||
|
||||
async _onRender(context, options) {
|
||||
await super._onRender(context, options);
|
||||
const html = $(this.element);
|
||||
html.find('.mgt2-cal-config-btn').on('click', () => {
|
||||
this._onConfig?.();
|
||||
});
|
||||
}
|
||||
|
||||
updateDisplay(data) {
|
||||
this._data = { ...this._data, ...data };
|
||||
this.render();
|
||||
}
|
||||
|
||||
close() {
|
||||
this._onConfig = null;
|
||||
this._onClose?.();
|
||||
this._onClose = null;
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import { getDayLabel, computeConversions } from './calendarDate.js';
|
||||
|
||||
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
const MODULE_ID = 'mgt2-compendium-amiral-denisov';
|
||||
|
||||
export class CalendarConfigDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
static DEFAULT_OPTIONS = {
|
||||
id: 'mgt2-calendar-config',
|
||||
classes: ['mgt2-calendar'],
|
||||
position: { width: 300, height: 'auto' },
|
||||
window: {
|
||||
icon: 'fas fa-cog',
|
||||
title: 'Configurer le calendrier',
|
||||
resizable: false,
|
||||
},
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: `modules/${MODULE_ID}/templates/calendar-config.hbs`,
|
||||
root: true,
|
||||
},
|
||||
};
|
||||
|
||||
constructor({ data, onApply } = {}) {
|
||||
super();
|
||||
this._data = { ...data };
|
||||
this._onApply = onApply;
|
||||
}
|
||||
|
||||
get title() {
|
||||
return 'Configurer le calendrier';
|
||||
}
|
||||
|
||||
async _prepareContext() {
|
||||
return {
|
||||
year: this._data.year,
|
||||
day: this._data.day,
|
||||
hour: this._data.hour,
|
||||
minute: this._data.minute,
|
||||
planet: this._data.planet ?? '',
|
||||
dayLabel: getDayLabel(this._data.day),
|
||||
conversions: computeConversions(this._data.year),
|
||||
};
|
||||
}
|
||||
|
||||
async _onRender(context, options) {
|
||||
await super._onRender(context, options);
|
||||
const html = $(this.element);
|
||||
|
||||
html.find('[name="day"]').on('input', () => {
|
||||
const day = parseInt(html.find('[name="day"]').val(), 10);
|
||||
if (day >= 1 && day <= 365) {
|
||||
html.find('.mgt2-cal-day-label').text(getDayLabel(day));
|
||||
}
|
||||
});
|
||||
|
||||
html.find('[name="year"]').on('input', () => {
|
||||
const year = parseInt(html.find('[name="year"]').val(), 10) || 0;
|
||||
const conv = computeConversions(year);
|
||||
html.find('.mgt2-cal-conv-val[data-cal="solomani"]').text(conv.solomani);
|
||||
html.find('.mgt2-cal-conv-val[data-cal="vilani"]').text(conv.vilani);
|
||||
html.find('.mgt2-cal-conv-val[data-cal="zhodani"]').text(conv.zhodani);
|
||||
html.find('.mgt2-cal-conv-val[data-cal="aslan"]').text(conv.aslan);
|
||||
html.find('.mgt2-cal-conv-val[data-cal="kkree"]').text(conv.kkree);
|
||||
html.find('.mgt2-cal-conv-val[data-cal="hiver"]').text(conv.hiver);
|
||||
});
|
||||
|
||||
html.find('[data-action="day-inc"]').on('click', () => {
|
||||
const input = html.find('[name="day"]');
|
||||
let day = parseInt(input.val(), 10) || 1;
|
||||
day = day >= 365 ? 1 : day + 1;
|
||||
input.val(day);
|
||||
html.find('.mgt2-cal-day-label').text(getDayLabel(day));
|
||||
});
|
||||
|
||||
html.find('[data-action="hour-inc"]').on('click', () => {
|
||||
const dayInput = html.find('[name="day"]');
|
||||
const hourInput = html.find('[name="hour"]');
|
||||
const minuteInput = html.find('[name="minute"]');
|
||||
let day = parseInt(dayInput.val(), 10) || 1;
|
||||
let hour = parseInt(hourInput.val(), 10) || 0;
|
||||
let minute = parseInt(minuteInput.val(), 10) || 0;
|
||||
|
||||
hour += 1;
|
||||
if (hour >= 24) {
|
||||
hour = 0;
|
||||
day = day >= 365 ? 1 : day + 1;
|
||||
}
|
||||
|
||||
hourInput.val(hour);
|
||||
dayInput.val(day);
|
||||
html.find('.mgt2-cal-day-label').text(getDayLabel(day));
|
||||
});
|
||||
|
||||
html.find('[data-action="apply"]').on('click', () => {
|
||||
this._readAndApply(html);
|
||||
});
|
||||
}
|
||||
|
||||
_readAndApply(html) {
|
||||
const year = parseInt(html.find('[name="year"]').val(), 10) || 1116;
|
||||
const day = Math.min(365, Math.max(1, parseInt(html.find('[name="day"]').val(), 10) || 1));
|
||||
const hour = Math.min(23, Math.max(0, parseInt(html.find('[name="hour"]').val(), 10) || 0));
|
||||
const minute = Math.min(59, Math.max(0, parseInt(html.find('[name="minute"]').val(), 10) || 0));
|
||||
const planet = html.find('[name="planet"]').val()?.trim() ?? '';
|
||||
|
||||
const data = { year, day, hour, minute, planet };
|
||||
this._onApply?.(data);
|
||||
}
|
||||
|
||||
close() {
|
||||
this._onApply = null;
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
const MODULE_ID = 'mgt2-compendium-amiral-denisov';
|
||||
|
||||
export function imperialToSolomani(impYear) {
|
||||
return Math.round(impYear * 1.0006644 + 4521);
|
||||
}
|
||||
|
||||
export function imperialToVilani(impYear) {
|
||||
return Math.round((impYear + 4045) / 1.312);
|
||||
}
|
||||
|
||||
export function imperialToZhodani(impYear) {
|
||||
const raw = (impYear + 6731) / 2.26;
|
||||
const olympiad = Math.floor(raw);
|
||||
const frac = raw - olympiad;
|
||||
const year = Math.min(3, Math.max(1, Math.round(frac * 3)));
|
||||
return { olympiad, year, label: `${olympiad}.${year}` };
|
||||
}
|
||||
|
||||
export function imperialToAslan(impYear) {
|
||||
return Math.round((impYear + 2083) / 0.877);
|
||||
}
|
||||
|
||||
export function imperialToKkree(impYear) {
|
||||
return Math.round((impYear + 8145) / 1.2);
|
||||
}
|
||||
|
||||
export function imperialToHiver(impYear) {
|
||||
return Math.round((impYear - 410) * 2);
|
||||
}
|
||||
|
||||
export function computeConversions(impYear) {
|
||||
const y = impYear ?? 1116;
|
||||
const z = imperialToZhodani(y);
|
||||
return {
|
||||
solomani: `${imperialToSolomani(y)} AD`,
|
||||
vilani: `${imperialToVilani(y)} VI`,
|
||||
zhodani: z.label,
|
||||
aslan: `${imperialToAslan(y)}`,
|
||||
kkree: `${imperialToKkree(y)}`,
|
||||
hiver: `${imperialToHiver(y)} HF`,
|
||||
};
|
||||
}
|
||||
|
||||
export const DAY_NAMES = [
|
||||
'Holiday',
|
||||
'Wonday',
|
||||
'Tuday',
|
||||
'Thirday',
|
||||
'Forday',
|
||||
'Fiday',
|
||||
'Sixday',
|
||||
'Senday',
|
||||
];
|
||||
|
||||
export const DAY_LABELS_FR = {
|
||||
Holiday: 'Holiday',
|
||||
Wonday: 'Wonday',
|
||||
Tuday: 'Tuday',
|
||||
Thirday: 'Thirday',
|
||||
Forday: 'Forday',
|
||||
Fiday: 'Fiday',
|
||||
Sixday: 'Sixday',
|
||||
Senday: 'Senday',
|
||||
};
|
||||
|
||||
export function getDayLabel(day) {
|
||||
if (day < 1 || day > 365) return '?';
|
||||
if (day === 1) return DAY_NAMES[0];
|
||||
return DAY_NAMES[((day - 2) % 7) + 1];
|
||||
}
|
||||
|
||||
export function getWeekday(day) {
|
||||
if (day < 1 || day > 365) return -1;
|
||||
if (day === 1) return -1;
|
||||
return (day - 2) % 7;
|
||||
}
|
||||
|
||||
export function formatImperialDate({ year, day, hour, minute, planet } = {}) {
|
||||
const y = year ?? 1116;
|
||||
const d = day ?? 1;
|
||||
const dayLabel = getDayLabel(d);
|
||||
const dayStr = String(d).padStart(3, '0');
|
||||
let label = `${dayLabel}, ${dayStr}-${y} IC`;
|
||||
if (hour != null && minute != null) {
|
||||
const h = String(Math.round(hour)).padStart(2, '0');
|
||||
const m = String(Math.round(minute)).padStart(2, '0');
|
||||
label += ` ─ ${h}:${m}`;
|
||||
if (planet) label += ` (${planet})`;
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
export function getDefaultCalendar() {
|
||||
return { year: 1116, day: 1, hour: 12, minute: 0, planet: '' };
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
import { CalendarApp } from './CalendarApp.js';
|
||||
import { CalendarConfigDialog } from './CalendarConfigDialog.js';
|
||||
import { getDefaultCalendar } from './calendarDate.js';
|
||||
|
||||
const MODULE_ID = 'mgt2-compendium-amiral-denisov';
|
||||
const ChatLogV2 = foundry.applications.sidebar.tabs.ChatLog;
|
||||
|
||||
let _calendarApp = null;
|
||||
|
||||
function readCalendar() {
|
||||
return {
|
||||
year: game.settings.get(MODULE_ID, 'calendarYear'),
|
||||
day: game.settings.get(MODULE_ID, 'calendarDay'),
|
||||
hour: game.settings.get(MODULE_ID, 'calendarHour'),
|
||||
minute: game.settings.get(MODULE_ID, 'calendarMinute'),
|
||||
planet: game.settings.get(MODULE_ID, 'calendarPlanet'),
|
||||
};
|
||||
}
|
||||
|
||||
async function writeCalendar(data) {
|
||||
await game.settings.set(MODULE_ID, 'calendarYear', data.year);
|
||||
await game.settings.set(MODULE_ID, 'calendarDay', data.day);
|
||||
await game.settings.set(MODULE_ID, 'calendarHour', data.hour);
|
||||
await game.settings.set(MODULE_ID, 'calendarMinute', data.minute);
|
||||
await game.settings.set(MODULE_ID, 'calendarPlanet', data.planet);
|
||||
}
|
||||
|
||||
function applyCalendar(data) {
|
||||
if (_calendarApp) {
|
||||
_calendarApp.updateDisplay(data);
|
||||
}
|
||||
}
|
||||
|
||||
async function broadcastCalendar(data) {
|
||||
if (!game.socket?.emit) return;
|
||||
game.socket.emit(`module.${MODULE_ID}`, {
|
||||
_type: 'calendarSync',
|
||||
...data,
|
||||
});
|
||||
}
|
||||
|
||||
function openCalendarApp() {
|
||||
if (_calendarApp) {
|
||||
_calendarApp.bringToTop();
|
||||
return;
|
||||
}
|
||||
const data = readCalendar();
|
||||
_calendarApp = new CalendarApp({
|
||||
onConfig: () => openCalendarConfig(),
|
||||
onClose: () => { _calendarApp = null; },
|
||||
});
|
||||
_calendarApp.updateDisplay(data);
|
||||
_calendarApp.render({ force: true });
|
||||
}
|
||||
|
||||
function openCalendarConfig() {
|
||||
const data = readCalendar();
|
||||
const dialog = new CalendarConfigDialog({
|
||||
data,
|
||||
onApply: async (newData) => {
|
||||
await writeCalendar(newData);
|
||||
applyCalendar(newData);
|
||||
broadcastCalendar(newData);
|
||||
dialog.close();
|
||||
},
|
||||
});
|
||||
dialog.render({ force: true });
|
||||
}
|
||||
|
||||
function registerCalCommand() {
|
||||
if (!ChatLogV2?.CHAT_COMMANDS) return;
|
||||
ChatLogV2.CHAT_COMMANDS.cal = {
|
||||
rgx: /^\/cal(?:\s+(.*))?$/i,
|
||||
fn: () => {
|
||||
openCalendarApp();
|
||||
return false;
|
||||
},
|
||||
};
|
||||
console.log(`${MODULE_ID} | Commande /cal enregistrée via ChatLog.CHAT_COMMANDS`);
|
||||
}
|
||||
|
||||
Hooks.once('init', () => {
|
||||
const def = getDefaultCalendar();
|
||||
|
||||
game.settings.register(MODULE_ID, 'calendarYear', {
|
||||
name: 'Année impériale',
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: Number,
|
||||
default: def.year,
|
||||
});
|
||||
game.settings.register(MODULE_ID, 'calendarDay', {
|
||||
name: 'Jour impérial (1-365)',
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: Number,
|
||||
default: def.day,
|
||||
});
|
||||
game.settings.register(MODULE_ID, 'calendarHour', {
|
||||
name: 'Heure locale',
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: Number,
|
||||
default: def.hour,
|
||||
});
|
||||
game.settings.register(MODULE_ID, 'calendarMinute', {
|
||||
name: 'Minute locale',
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: Number,
|
||||
default: def.minute,
|
||||
});
|
||||
game.settings.register(MODULE_ID, 'calendarPlanet', {
|
||||
name: 'Planète',
|
||||
scope: 'world',
|
||||
config: false,
|
||||
type: String,
|
||||
default: def.planet,
|
||||
});
|
||||
game.settings.register(MODULE_ID, 'calendarAutoOpen', {
|
||||
name: 'Calendrier impérial : auto-ouverture',
|
||||
hint: 'Ouvre automatiquement la fenêtre du calendrier au démarrage',
|
||||
scope: 'world',
|
||||
config: true,
|
||||
type: Boolean,
|
||||
default: true,
|
||||
});
|
||||
|
||||
const loadTemplatesFn = foundry.applications?.handlebars?.loadTemplates || loadTemplates;
|
||||
if (loadTemplatesFn) {
|
||||
loadTemplatesFn([
|
||||
`modules/${MODULE_ID}/templates/calendar-app.hbs`,
|
||||
`modules/${MODULE_ID}/templates/calendar-config.hbs`,
|
||||
]);
|
||||
}
|
||||
|
||||
registerCalCommand();
|
||||
console.log(`${MODULE_ID} | Calendrier impérial initialisé`);
|
||||
});
|
||||
|
||||
Hooks.once('ready', () => {
|
||||
game.socket.on(`module.${MODULE_ID}`, (data) => {
|
||||
if (game.user?.isGM) return;
|
||||
if (data?._type !== 'calendarSync') return;
|
||||
if (!_calendarApp) {
|
||||
const { year, day, hour, minute, planet } = data;
|
||||
_calendarApp = new CalendarApp({
|
||||
onClose: () => { _calendarApp = null; },
|
||||
});
|
||||
_calendarApp.updateDisplay({ year, day, hour, minute, planet });
|
||||
_calendarApp.render({ force: true });
|
||||
return;
|
||||
}
|
||||
_calendarApp.updateDisplay(data);
|
||||
});
|
||||
|
||||
if (game.settings.get(MODULE_ID, 'calendarAutoOpen')) {
|
||||
openCalendarApp();
|
||||
}
|
||||
});
|
||||
|
||||
Hooks.on('preCreateChatMessage', (message, data, options) => {
|
||||
const content = message.content?.trim()?.toLowerCase();
|
||||
if (content === '/cal' || content?.startsWith('/cal ')) {
|
||||
openCalendarApp();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
Hooks.on('chatMessage', (...args) => {
|
||||
let message;
|
||||
if (args[0]?.content !== undefined) {
|
||||
message = args[0].content;
|
||||
} else if (typeof args[1] === 'string') {
|
||||
message = args[1];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
const trimmed = message?.trim()?.toLowerCase();
|
||||
if (trimmed === '/cal' || trimmed?.startsWith('/cal ')) {
|
||||
openCalendarApp();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
Hooks.on('renderChatInput', (app, html, data) => {
|
||||
let input;
|
||||
if (html?.element) {
|
||||
input = $(html.element).find('textarea[name="content"]');
|
||||
} else if (html?.find) {
|
||||
input = html.find('textarea[name="content"]');
|
||||
} else {
|
||||
input = $(html).find('textarea[name="content"]');
|
||||
}
|
||||
if (input.data('mgt2-cal-listener')) return;
|
||||
input.data('mgt2-cal-listener', true);
|
||||
input.on('keydown', (event) => {
|
||||
if (event.key === 'Enter' && !event.shiftKey) {
|
||||
const content = input.val()?.trim();
|
||||
if (content === '/cal' || content?.startsWith('/cal ')) {
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
openCalendarApp();
|
||||
input.val('');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -12,7 +12,7 @@ function buildStartupNoticeHtml() {
|
||||
<div style="padding:6px 8px;border-left:4px solid #c9a227;background:#f5f0e8;color:#2c2c3e;line-height:1.45;">
|
||||
<strong>MgT2e – outils</strong>
|
||||
<span style="color:#6a5422;">·</span>
|
||||
<code>/commerce</code> <code>/pnj</code> <code>/rencontre</code> <code>/mission</code> <code>/sector</code>
|
||||
<code>/cal</code> <code>/commerce</code> <code>/pnj</code> <code>/rencontre</code> <code>/mission</code> <code>/sector</code>
|
||||
<div style="margin-top:2px;font-size:0.92em;color:#6a5422;">
|
||||
par <a href="https://www.uberwald.me" target="_blank" rel="noopener noreferrer">LeRatierBretonnien / Uberwald</a>
|
||||
</div>
|
||||
|
||||
+190
@@ -1162,3 +1162,193 @@ a.mgt2-world-link:hover {
|
||||
color: #555;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* === Calendrier Impérial =================================================== */
|
||||
|
||||
.mgt2-calendar .window-header {
|
||||
background: linear-gradient(180deg, rgba(44, 44, 62, 0.96) 0%, rgba(30, 30, 43, 0.96) 100%);
|
||||
border-bottom: 1px solid #c9a227;
|
||||
}
|
||||
|
||||
.mgt2-calendar .window-title {
|
||||
color: #d9b24c;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.mgt2-calendar .window-content {
|
||||
padding: 4px 8px;
|
||||
background: #1a1a2e;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mgt2-cal-window {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
min-height: 28px;
|
||||
}
|
||||
|
||||
.mgt2-cal-display {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.mgt2-cal-date {
|
||||
color: #d8c79a;
|
||||
font-size: 0.95em;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mgt2-cal-config-btn {
|
||||
flex: 0 0 auto;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: #8a7a5a;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.85em;
|
||||
border-radius: 3px;
|
||||
transition: color 0.15s, background 0.15s;
|
||||
}
|
||||
|
||||
.mgt2-cal-config-btn:hover {
|
||||
color: #d9b24c;
|
||||
background: rgba(201, 162, 39, 0.15);
|
||||
}
|
||||
|
||||
/* Config dialog */
|
||||
|
||||
.mgt2-cal-config {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 8px;
|
||||
background: #1a1a2e;
|
||||
color: #d8c79a;
|
||||
}
|
||||
|
||||
.mgt2-cal-config-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.mgt2-cal-config-row label {
|
||||
flex: 0 0 80px;
|
||||
font-size: 0.85em;
|
||||
color: #b8a87a;
|
||||
}
|
||||
|
||||
.mgt2-cal-config-row input[type="number"],
|
||||
.mgt2-cal-config-row input[type="text"] {
|
||||
flex: 1;
|
||||
padding: 3px 6px;
|
||||
border: 1px solid #3a3a4e;
|
||||
border-radius: 3px;
|
||||
background: #252540;
|
||||
color: #d8c79a;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
.mgt2-cal-config-row input:focus {
|
||||
outline: none;
|
||||
border-color: #c9a227;
|
||||
}
|
||||
|
||||
.mgt2-cal-day-group {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.mgt2-cal-day-group input {
|
||||
flex: 0 0 60px;
|
||||
}
|
||||
|
||||
.mgt2-cal-day-label {
|
||||
font-size: 0.85em;
|
||||
color: #d9b24c;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mgt2-cal-config-actions {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
justify-content: flex-end;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.mgt2-cal-btn {
|
||||
padding: 4px 10px;
|
||||
border: 1px solid #3a3a4e;
|
||||
border-radius: 3px;
|
||||
background: #252540;
|
||||
color: #d8c79a;
|
||||
font-size: 0.82em;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s, border-color 0.15s;
|
||||
}
|
||||
|
||||
.mgt2-cal-btn:hover {
|
||||
background: #303050;
|
||||
border-color: #c9a227;
|
||||
}
|
||||
|
||||
.mgt2-cal-btn-primary {
|
||||
background: linear-gradient(180deg, #c9a227 0%, #a8881e 100%);
|
||||
color: #1a1a2e;
|
||||
border-color: #c9a227;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.mgt2-cal-btn-primary:hover {
|
||||
background: linear-gradient(180deg, #d9b24c 0%, #c9a227 100%);
|
||||
}
|
||||
|
||||
.mgt2-cal-conversions {
|
||||
margin-top: 8px;
|
||||
border: 1px solid #3a3a4e;
|
||||
border-radius: 3px;
|
||||
background: #1e1e32;
|
||||
}
|
||||
|
||||
.mgt2-cal-conversions summary {
|
||||
padding: 4px 8px;
|
||||
color: #b8a87a;
|
||||
font-size: 0.82em;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.mgt2-cal-conversions summary:hover {
|
||||
color: #d9b24c;
|
||||
}
|
||||
|
||||
.mgt2-cal-conv-note {
|
||||
font-size: 0.85em;
|
||||
color: #6a6a7e;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.mgt2-cal-conv-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 2px 8px;
|
||||
font-size: 0.82em;
|
||||
color: #d8c79a;
|
||||
}
|
||||
|
||||
.mgt2-cal-conv-row:nth-child(even) {
|
||||
background: rgba(255,255,255,0.03);
|
||||
}
|
||||
|
||||
.mgt2-cal-conv-label {
|
||||
color: #8a7a5a;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<div class="mgt2-cal-window">
|
||||
<div class="mgt2-cal-display">
|
||||
<span class="mgt2-cal-date">{{dateLabel}}</span>
|
||||
</div>
|
||||
{{#if isGM}}
|
||||
<button type="button" class="mgt2-cal-config-btn" title="Configurer le calendrier">
|
||||
<i class="fas fa-cog"></i>
|
||||
</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
@@ -0,0 +1,39 @@
|
||||
<form class="mgt2-cal-config">
|
||||
<div class="mgt2-cal-config-row">
|
||||
<label for="cal-year">Année</label>
|
||||
<input type="number" id="cal-year" name="year" value="{{year}}" min="0" max="9999">
|
||||
</div>
|
||||
<div class="mgt2-cal-config-row">
|
||||
<label for="cal-day">Jour</label>
|
||||
<div class="mgt2-cal-day-group">
|
||||
<input type="number" id="cal-day" name="day" value="{{day}}" min="1" max="365">
|
||||
<span class="mgt2-cal-day-label">{{dayLabel}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mgt2-cal-config-row">
|
||||
<label for="cal-hour">Heure</label>
|
||||
<input type="number" id="cal-hour" name="hour" value="{{hour}}" min="0" max="23">
|
||||
</div>
|
||||
<div class="mgt2-cal-config-row">
|
||||
<label for="cal-minute">Minute</label>
|
||||
<input type="number" id="cal-minute" name="minute" value="{{minute}}" min="0" max="59">
|
||||
</div>
|
||||
<div class="mgt2-cal-config-row">
|
||||
<label for="cal-planet">Planète</label>
|
||||
<input type="text" id="cal-planet" name="planet" value="{{planet}}" placeholder="p. ex. Terra">
|
||||
</div>
|
||||
<div class="mgt2-cal-config-actions">
|
||||
<button type="button" class="mgt2-cal-btn" data-action="day-inc"><i class="fas fa-plus"></i> +1 jour</button>
|
||||
<button type="button" class="mgt2-cal-btn" data-action="hour-inc"><i class="fas fa-plus"></i> +1 heure</button>
|
||||
<button type="button" class="mgt2-cal-btn mgt2-cal-btn-primary" data-action="apply">Appliquer</button>
|
||||
</div>
|
||||
<details class="mgt2-cal-conversions">
|
||||
<summary>Conversions calendaires <span class="mgt2-cal-conv-note">(approximatif)</span></summary>
|
||||
<div class="mgt2-cal-conv-row"><span class="mgt2-cal-conv-label">Solomani</span><span class="mgt2-cal-conv-val" data-cal="solomani">{{conversions.solomani}}</span></div>
|
||||
<div class="mgt2-cal-conv-row"><span class="mgt2-cal-conv-label">Vilani</span><span class="mgt2-cal-conv-val" data-cal="vilani">{{conversions.vilani}}</span></div>
|
||||
<div class="mgt2-cal-conv-row"><span class="mgt2-cal-conv-label">Zhodani</span><span class="mgt2-cal-conv-val" data-cal="zhodani">{{conversions.zhodani}}</span></div>
|
||||
<div class="mgt2-cal-conv-row"><span class="mgt2-cal-conv-label">Aslan</span><span class="mgt2-cal-conv-val" data-cal="aslan">{{conversions.aslan}}</span></div>
|
||||
<div class="mgt2-cal-conv-row"><span class="mgt2-cal-conv-label">K'Kree</span><span class="mgt2-cal-conv-val" data-cal="kkree">{{conversions.kkree}}</span></div>
|
||||
<div class="mgt2-cal-conv-row"><span class="mgt2-cal-conv-label">Hiver</span><span class="mgt2-cal-conv-val" data-cal="hiver">{{conversions.hiver}}</span></div>
|
||||
</details>
|
||||
</form>
|
||||
Reference in New Issue
Block a user