@threestarsoftware/tssgrid 0.1.29-beta.1 → 0.1.29-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -0
- package/package.json +1 -1
- package/plugins/tss-calendar.css +4 -0
- package/plugins/tss-calendar.d.ts +9 -3
- package/plugins/tss-calendar.js +25 -14
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,7 @@ TssGrid の各リリースの変更点。日付は JST。形式は [Keep a Chang
|
|
|
7
7
|
|
|
8
8
|
### 追加(プラグイン)
|
|
9
9
|
- **`tss-calendar` に範囲選択 `range` を追加** — 連続した日(休暇申請の開始〜終了など)を**1回の編集で**選べる。`range:true` で有効、`range:(ctx)=>boolean` で**行ごとにON/OFF**(半休の行は範囲を選ばせない等)。未指定は単日=**従来動作**。1クリック目=開始・2クリック目=終了(**開始より前なら入替・同じ日を2回で単日確定**)、間の日に色が付く。1クリック目だけで閉じたら(Esc/外クリック)**値は変えない**。確定値は既定で `2026/10/13~2026/10/16`(**単日は ISO `2026-10-13`=範囲なしの単日と同じ形**で保存値が混ざらない/区切りは `rangeSeparator`・既定 '~'/`rangeValue(startISO,endISO)` で完全差替可・**同日も呼ばれる**)。**入力中のセルに範囲ツールチップ**を出し、`rangeTooltip(startISO,endISO)` で文面を差し替え可能(休日を除いた日数などを外から足す口)。**休日スキップは利用側**=カレンダーは範囲を返すだけ。範囲列は `type:'date'` を付けない素の列にする(範囲文字列を日付パーサに通さないため)。例 `examples/calendar-range.html`。
|
|
10
|
+
- **`tss-calendar` 範囲に「範囲で選ぶ」トグルを追加** — `range` が有効な行のカレンダー上部にトグル(チェックボックス)を出し、**OFF=従来の単日**(1クリック確定・値 ISO・ツールチップ無し)/**ON=範囲**(2クリック・間の色・ツールチップ)を編集中に切替可能。休暇申請はほとんどが1日なので**既定 OFF**(1クリックで決まる)。初期状態は `rangeDefault`(`true`/`(ctx)=>boolean`・既定 `false`)で、**どちらでもセル値が既に範囲なら ON で開く**。`range:(ctx)=>false` の行はトグルを出さない。文言は `rangeToggleLabel`(既定 `'範囲で選ぶ'`)。
|
|
10
11
|
|
|
11
12
|
## [0.1.28] - 2026-10-20
|
|
12
13
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@threestarsoftware/tssgrid",
|
|
3
|
-
"version": "0.1.29-beta.
|
|
3
|
+
"version": "0.1.29-beta.2",
|
|
4
4
|
"description": "軽量・ノービルド・日本語IME堅牢な編集グリッド(vanilla JS / 依存なし / MIT)。Excel ライクな入力画面を <script> タグ1本で。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "ThreeStarSoftware (https://www.threestarsoftware.co.jp/)",
|
package/plugins/tss-calendar.css
CHANGED
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
border: 1px solid var(--cal-border); border-radius: var(--cal-radius);
|
|
19
19
|
box-shadow: 0 6px 24px rgba(0,0,0,.18); padding: 8px; font: var(--cal-font); user-select: none; outline: none;
|
|
20
20
|
}
|
|
21
|
+
/* 範囲選択トグル(range 有効行のみ上部に表示)。 */
|
|
22
|
+
.tss-cal-toggle { display: flex; align-items: center; gap: 6px; margin: 0 2px 6px; padding: 3px 4px; font-size: 12px; color: var(--cal-fg); cursor: pointer; border-bottom: 1px solid var(--cal-border); user-select: none; }
|
|
23
|
+
.tss-cal-toggle input { cursor: pointer; margin: 0; }
|
|
24
|
+
.tss-cal-toggle:hover { color: var(--cal-accent); }
|
|
21
25
|
.tss-cal-head { display: flex; align-items: center; justify-content: space-between; margin: 2px 2px 6px; }
|
|
22
26
|
.tss-cal-title { font-weight: 600; }
|
|
23
27
|
.tss-cal-sels { display: flex; gap: 4px; }
|
|
@@ -28,10 +28,16 @@ declare namespace TssCalendar {
|
|
|
28
28
|
disable?: Array<'holiday' | 'weekend' | string>;
|
|
29
29
|
/** 共有 input に乗る型(既定 true)=直打ち可・矢印は本体へ。false でフォーカス奪取型。 */
|
|
30
30
|
inline?: boolean;
|
|
31
|
-
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
31
|
+
/** 範囲選択を**使える行**にする。`true` で全行/`(ctx)=>boolean` で行ごと(例: 半休の行は `false`=単日のみ)。
|
|
32
|
+
* 有効な行では上部に「範囲で選ぶ」トグルが出る: **OFF=従来の単日**(1クリック確定・値 ISO・ツールチップ無し)/
|
|
33
|
+
* **ON=範囲**(1クリック目=開始・2クリック目=終了・開始より前なら入替・同日2回は単日・間の色・ツールチップ)。
|
|
34
|
+
* 未指定は常に単日=従来動作。初期ON/OFFは `rangeDefault`、確定値・ツールチップ・文言は下記で差替可。休日スキップは利用側。 */
|
|
34
35
|
range?: boolean | ((ctx: any) => boolean);
|
|
36
|
+
/** 「範囲で選ぶ」トグルの初期状態(`true`=ON/`(ctx)=>boolean` で行ごと・既定 `false`=OFF)。
|
|
37
|
+
* いずれの場合も **セルの値が既に範囲なら ON で開く**。`range` が無効な行では無視(トグル自体が出ない)。 */
|
|
38
|
+
rangeDefault?: boolean | ((ctx: any) => boolean);
|
|
39
|
+
/** トグルの文言(既定 `'範囲で選ぶ'`)。 */
|
|
40
|
+
rangeToggleLabel?: string;
|
|
35
41
|
/** 範囲値・ツールチップの日付区切り(既定 '~')。 */
|
|
36
42
|
rangeSeparator?: string;
|
|
37
43
|
/** 範囲の確定値を作る。**同日(単日)のときも呼ばれる**。引数は開始・終了の ISO
|
package/plugins/tss-calendar.js
CHANGED
|
@@ -38,12 +38,15 @@
|
|
|
38
38
|
var inline = opts.inline !== false; // true=共有 input に乗る(直打ち可・矢印は本体に返す)/false=従来のフォーカス奪取型
|
|
39
39
|
|
|
40
40
|
// ── 範囲選択(range): 連続した日を1回の編集で選ぶ(休暇申請の開始〜終了 等)───────────
|
|
41
|
-
// range:true
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
41
|
+
// range:true で「範囲も選べる」行になる(行ごとに切替えたい時は range:(ctx)=>boolean)。未指定は常に単日=従来動作。
|
|
42
|
+
// range が有効な行では、カレンダー上部に「範囲で選ぶ」トグルが出る(文言は rangeToggleLabel):
|
|
43
|
+
// OFF=従来の単日(1クリックで確定・値は ISO・ツールチップ無し)/ON=2クリック(開始→終了・間の色・ツールチップ)。
|
|
44
|
+
// トグルの初期状態は rangeDefault(true/関数(ctx)=>boolean・既定 OFF)。ただし **セル値が既に範囲なら ON で開く**。
|
|
45
|
+
// ON 時の確定値は rangeValue(開始ISO,終了ISO)(既定: 範囲 'YYYY/MM/DD~YYYY/MM/DD'/同日は ISO 'YYYY-MM-DD'。rangeValue は同日も呼ぶ)。
|
|
45
46
|
// 休日スキップは利用側(返すのは範囲だけ)。
|
|
46
47
|
var rangeOpt = opts.range;
|
|
48
|
+
var rangeDefault = opts.rangeDefault; // トグルの初期ON/OFF(bool or (ctx)=>bool・既定 false=OFF)
|
|
49
|
+
var rangeToggleLabel = opts.rangeToggleLabel != null ? opts.rangeToggleLabel : '範囲で選ぶ';
|
|
47
50
|
var rangeSep = opts.rangeSeparator != null ? opts.rangeSeparator : '~';
|
|
48
51
|
var rangeValueFn = typeof opts.rangeValue === 'function' ? opts.rangeValue : null;
|
|
49
52
|
var rangeTooltipFn = typeof opts.rangeTooltip === 'function' ? opts.rangeTooltip : null;
|
|
@@ -53,8 +56,11 @@
|
|
|
53
56
|
return aIso === bIso ? aIso : defRangeStr(aIso, bIso); // 既定: 単日=ISO(範囲なしと同形)/範囲='YYYY/MM/DD~YYYY/MM/DD'
|
|
54
57
|
}
|
|
55
58
|
function rangeTipOf(aIso, bIso) { return rangeTooltipFn ? rangeTooltipFn(aIso, bIso) : (aIso === bIso ? fmtSlash(aIso) : defRangeStr(aIso, bIso)); }
|
|
56
|
-
|
|
59
|
+
function isRangeValue(v) { v = String(v == null ? '' : v); return !!rangeSep && v.indexOf(rangeSep) >= 0; } // 既定書式の範囲値か(区切りを含む)
|
|
60
|
+
var rc = null; // この行で範囲が使えるなら { td, def } を持つ=トグルを出す(open(ctx) で ctx 判定)
|
|
61
|
+
var rangeActive = false; // トグルの現在値(ON=2クリック範囲/OFF=単日)。rc 無しの行は常に false
|
|
57
62
|
var rangeStart = null, hoverEnd = null; // 範囲選択の途中状態(1クリック目=rangeStart、プレビュー終端=hoverEnd)
|
|
63
|
+
function setRangeActive(on) { rangeActive = !!on; rangeStart = null; hoverEnd = null; clearTip(); render(); } // トグル切替=途中選択はリセット
|
|
58
64
|
|
|
59
65
|
function disabled(d) {
|
|
60
66
|
if (minD && d < minD) return true;
|
|
@@ -74,7 +80,9 @@
|
|
|
74
80
|
var monSel = '<select class="tss-cal-sel-mon" aria-label="月">';
|
|
75
81
|
for (var mm = 0; mm < 12; mm++) monSel += '<option value="' + mm + '"' + (mm === m ? ' selected' : '') + '>' + (mm + 1) + '月</option>';
|
|
76
82
|
monSel += '</select>';
|
|
77
|
-
|
|
83
|
+
// range が使える行はトグル(範囲で選ぶ)を上部に。OFF=単日1クリック/ON=2クリック範囲。
|
|
84
|
+
var toggle = rc ? '<label class="tss-cal-toggle"><input type="checkbox" class="tss-cal-range-cb"' + (rangeActive ? ' checked' : '') + '><span>' + esc(rangeToggleLabel) + '</span></label>' : '';
|
|
85
|
+
var html = toggle + '<div class="tss-cal-head">'
|
|
78
86
|
+ '<button type="button" class="tss-cal-nav" data-nav="-1" aria-label="前の月">‹</button>'
|
|
79
87
|
+ '<span class="tss-cal-sels">' + yrSel + monSel + '</span>'
|
|
80
88
|
+ '<button type="button" class="tss-cal-nav" data-nav="1" aria-label="次の月">›</button></div>'
|
|
@@ -92,7 +100,7 @@
|
|
|
92
100
|
if (sameDay(d, today)) cls += ' tss-cal-today';
|
|
93
101
|
if (sameDay(d, sel)) cls += ' tss-cal-sel';
|
|
94
102
|
if (sameDay(d, focus)) cls += ' tss-cal-focus';
|
|
95
|
-
if (rc && rangeStart) {
|
|
103
|
+
if (rc && rangeActive && rangeStart) { // 範囲の途中=開始〜(プレビュー)終端を塗る(ON のときだけ)
|
|
96
104
|
var ra = rangeStart, rb = hoverEnd || rangeStart, lo = ra <= rb ? ra : rb, hi = ra <= rb ? rb : ra;
|
|
97
105
|
if (d >= lo && d <= hi) cls += ' tss-cal-inrange';
|
|
98
106
|
if (sameDay(d, ra)) cls += ' tss-cal-range-start';
|
|
@@ -119,7 +127,8 @@
|
|
|
119
127
|
}
|
|
120
128
|
function onChange(e) {
|
|
121
129
|
var t = e.target;
|
|
122
|
-
if (t.classList && t.classList.contains('tss-cal-
|
|
130
|
+
if (t.classList && t.classList.contains('tss-cal-range-cb')) { setRangeActive(t.checked); } // 「範囲で選ぶ」トグル
|
|
131
|
+
else if (t.classList && t.classList.contains('tss-cal-sel-yr')) { view = new Date(+t.value, view.getMonth(), 1); render(); }
|
|
123
132
|
else if (t.classList && t.classList.contains('tss-cal-sel-mon')) { view = new Date(view.getFullYear(), +t.value, 1); render(); }
|
|
124
133
|
}
|
|
125
134
|
function onKey(e) {
|
|
@@ -129,12 +138,12 @@
|
|
|
129
138
|
if (k === 'Escape') { e.preventDefault(); cancel(); return; }
|
|
130
139
|
if (k === 'Enter') { e.preventDefault(); if (!disabled(focus)) pick(focus); return; }
|
|
131
140
|
var dx = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -7, ArrowDown: 7 }[k];
|
|
132
|
-
if (dx != null) { e.preventDefault(); focus = new Date(focus); focus.setDate(focus.getDate() + dx); view = new Date(focus.getFullYear(), focus.getMonth(), 1); if (rc && rangeStart) { hoverEnd = new Date(focus); updateTip(); } render(); return; }
|
|
141
|
+
if (dx != null) { e.preventDefault(); focus = new Date(focus); focus.setDate(focus.getDate() + dx); view = new Date(focus.getFullYear(), focus.getMonth(), 1); if (rc && rangeActive && rangeStart) { hoverEnd = new Date(focus); updateTip(); } render(); return; }
|
|
133
142
|
if (k === 'PageUp' || k === 'PageDown') { e.preventDefault(); view = new Date(view.getFullYear(), view.getMonth() + (k === 'PageUp' ? -1 : 1), 1); render(); }
|
|
134
143
|
}
|
|
135
144
|
// range 途中: 日の上をなぞると終端プレビューを更新(間の色+ツールチップ)。
|
|
136
145
|
function onOver(e) {
|
|
137
|
-
if (!(rc && rangeStart)) return;
|
|
146
|
+
if (!(rc && rangeActive && rangeStart)) return;
|
|
138
147
|
var dd = e.target.getAttribute && e.target.getAttribute('data-d');
|
|
139
148
|
if (!dd) return;
|
|
140
149
|
var d = parseISO(dd); if (!d || disabled(d)) return;
|
|
@@ -151,6 +160,7 @@
|
|
|
151
160
|
function show(anchor, value, onPick, onCancel, rangeCfg) {
|
|
152
161
|
teardown(); // 既に開いていれば先に片付ける(前の pop 除去+outside リスナ解除・open-while-open の取り残し防止)
|
|
153
162
|
done = false; rc = rangeCfg || null; rangeStart = null; hoverEnd = null;
|
|
163
|
+
rangeActive = rc ? (isRangeValue(value) || !!rc.def) : false; // 既存値が範囲 or rangeDefault なら ON で開く
|
|
154
164
|
sel = parseISO(value);
|
|
155
165
|
// range の既存値('YYYY/MM/DD~…' 等)は parseISO では取れない=先頭日を緩く読んで月を合わせる。
|
|
156
166
|
var initD = sel || (rc ? parseLoose(value) : null);
|
|
@@ -176,21 +186,22 @@
|
|
|
176
186
|
function teardown() { clearTip(); if (outside) { document.removeEventListener('mousedown', outside, true); outside = null; } if (pop) { pop.remove(); pop = null; } }
|
|
177
187
|
function pick(d) {
|
|
178
188
|
if (done) return;
|
|
179
|
-
if (rc) {
|
|
189
|
+
if (rc && rangeActive) { // トグル ON=2クリック範囲
|
|
180
190
|
if (!rangeStart) { rangeStart = d; hoverEnd = d; focus = new Date(d); render(); updateTip(); return; } // 1クリック目=開始(閉じない)
|
|
181
191
|
var aIso = iso(rangeStart), bIso = iso(d); // 2クリック目=終了
|
|
182
192
|
if (bIso < aIso) { var t = aIso; aIso = bIso; bIso = t; } // 逆順は入替/同日は単日
|
|
183
193
|
done = true; var fr = ref.pick; teardown(); fr(rangeValOf(aIso, bIso)); return;
|
|
184
194
|
}
|
|
185
|
-
done = true; var f = ref.pick; teardown(); f(iso(d));
|
|
195
|
+
done = true; var f = ref.pick; teardown(); f(iso(d)); // トグル OFF/range 無しの行=単日1クリック(ISO)
|
|
186
196
|
}
|
|
187
197
|
function cancel() { if (done) return; done = true; var f = ref.cancel; teardown(); if (f) f(); } // range 途中の取消も値は変えない(ref.cancel)
|
|
188
198
|
|
|
189
199
|
return {
|
|
190
200
|
// TssGrid カスタムエディタ契約
|
|
191
201
|
open: function (ctx) {
|
|
192
|
-
var rangeMode = typeof rangeOpt === 'function' ? !!rangeOpt(ctx) : !!rangeOpt; //
|
|
193
|
-
var
|
|
202
|
+
var rangeMode = typeof rangeOpt === 'function' ? !!rangeOpt(ctx) : !!rangeOpt; // この行で範囲を使えるか(半休行は false=トグル無し)
|
|
203
|
+
var def = typeof rangeDefault === 'function' ? !!rangeDefault(ctx) : !!rangeDefault; // トグル初期ON/OFF(行ごと可)
|
|
204
|
+
var cfg = rangeMode ? { td: ctx.td, def: def } : null;
|
|
194
205
|
show(ctx.td, ctx.value, function (v) { ctx.commit(v); }, function () { ctx.cancel(); }, cfg);
|
|
195
206
|
},
|
|
196
207
|
close: function () { teardown(); },
|