react-schedule-picker 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 innerbloo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,268 @@
1
+ # react-schedule-picker
2
+
3
+ [![npm version](https://img.shields.io/npm/v/react-schedule-picker.svg)](https://www.npmjs.com/package/react-schedule-picker)
4
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/react-schedule-picker)](https://bundlephobia.com/package/react-schedule-picker)
5
+ [![types](https://img.shields.io/npm/types/react-schedule-picker.svg)](https://www.npmjs.com/package/react-schedule-picker)
6
+ [![license](https://img.shields.io/npm/l/react-schedule-picker.svg)](./LICENSE)
7
+
8
+ Drag-to-select weekly schedule picker for React. Zero dependencies, fully typed, locale-aware.
9
+
10
+ **[Live Demo →](https://react-schedule-picker.vercel.app/)**
11
+
12
+ ```tsx
13
+ <SchedulePicker value={schedule} onChange={setSchedule} />
14
+ ```
15
+
16
+ ## Features
17
+
18
+ - **Drag selection** — click cells, drag rectangles, or drag day/hour headers to select rows/columns
19
+ - **Touch support** — full mobile drag with haptic feedback
20
+ - **Locale-aware** — built-in `en`, `en-US`, `ko`, `ja`, `zh-CN`, `zh-TW` (week start, hour format, weekend colors, button text)
21
+ - **Serializable** — convert to ranges or iCalendar-style payloads for APIs and databases
22
+ - **Themable** — CSS custom properties, no styling library required
23
+ - **Zero runtime dependencies** — only React as a peer dep
24
+ - **Fully typed** — written in TypeScript with strict types
25
+
26
+ ## Installation
27
+
28
+ ```bash
29
+ npm install react-schedule-picker
30
+ # or
31
+ pnpm add react-schedule-picker
32
+ # or
33
+ yarn add react-schedule-picker
34
+ # or
35
+ bun add react-schedule-picker
36
+ ```
37
+
38
+ Import the stylesheet once at your app root:
39
+
40
+ ```tsx
41
+ import "react-schedule-picker/styles.css";
42
+ ```
43
+
44
+ ## Quick Start
45
+
46
+ ```tsx
47
+ import { useState } from "react";
48
+ import { SchedulePicker, type Schedule } from "react-schedule-picker";
49
+ import "react-schedule-picker/styles.css";
50
+
51
+ export function Availability() {
52
+ const [schedule, setSchedule] = useState<Schedule>({});
53
+
54
+ return <SchedulePicker value={schedule} onChange={setSchedule} />;
55
+ }
56
+ ```
57
+
58
+ The `Schedule` shape is `Record<string, number[]>` — day key (`"mon"` … `"sun"`) mapped to an array of selected hours (0–23).
59
+
60
+ ## Common Examples
61
+
62
+ ### Localized to Korean
63
+
64
+ ```tsx
65
+ <SchedulePicker locale="ko" value={schedule} onChange={setSchedule} />
66
+ ```
67
+
68
+ ### Business hours only
69
+
70
+ ```tsx
71
+ <SchedulePicker
72
+ value={schedule}
73
+ onChange={setSchedule}
74
+ minHour={9}
75
+ maxHour={18}
76
+ visibleDays={["mon", "tue", "wed", "thu", "fri"]}
77
+ />
78
+ ```
79
+
80
+ ### Block out specific slots
81
+
82
+ ```tsx
83
+ const lunchBreak: Schedule = {
84
+ mon: [12, 13], tue: [12, 13], wed: [12, 13], thu: [12, 13], fri: [12, 13],
85
+ };
86
+
87
+ <SchedulePicker
88
+ value={schedule}
89
+ onChange={setSchedule}
90
+ disabledSlots={lunchBreak}
91
+ />
92
+ ```
93
+
94
+ ### Read-only display
95
+
96
+ ```tsx
97
+ <SchedulePicker value={schedule} onChange={setSchedule} readOnly />
98
+ ```
99
+
100
+ ### Submit-style — fire only when the user finishes dragging
101
+
102
+ ```tsx
103
+ <SchedulePicker
104
+ value={schedule}
105
+ onChange={setSchedule}
106
+ onSelectEnd={(final) => saveToServer(final)}
107
+ />
108
+ ```
109
+
110
+ ## Props
111
+
112
+ ### Core
113
+
114
+ | Prop | Type | Default | Description |
115
+ |------|------|---------|-------------|
116
+ | `value` | `Schedule` | required | Controlled schedule value |
117
+ | `onChange` | `(s: Schedule) => void` | required | Fires on every change, including during drag |
118
+ | `onSelectEnd` | `(s: Schedule) => void` | — | Fires once when a drag completes |
119
+
120
+ ### Localization
121
+
122
+ | Prop | Type | Default | Description |
123
+ |------|------|---------|-------------|
124
+ | `locale` | `"en" \| "en-US" \| "ko" \| "ja" \| "zh-CN" \| "zh-TW"` | `"en"` | Sets week start, hour format, weekend colors, and built-in text |
125
+ | `weekStartsOn` | `"mon" \| "sun" \| "sat"` | locale default | Override the first day of the week |
126
+ | `weekendHighlight` | `Record<string, string> \| "none"` | locale default | Weekend text colors. `"none"` disables highlighting |
127
+ | `dayLabels` | `Record<string, string>` | locale default | Custom day header labels |
128
+ | `messages` | `Partial<Messages>` | locale default | Override individual built-in messages |
129
+
130
+ ### Display
131
+
132
+ | Prop | Type | Default | Description |
133
+ |------|------|---------|-------------|
134
+ | `dayAxis` | `"x" \| "y"` | `"x"` | `"x"`: days as columns. `"y"`: days as rows |
135
+ | `visibleDays` | `string[]` | locale default | Which days to show, and their order |
136
+ | `minHour` | `number` | `0` | Earliest hour to display |
137
+ | `maxHour` | `number` | `23` | Latest hour to display |
138
+ | `compactHourLabels` | `boolean` | `false` | Show only every 3rd hour label |
139
+ | `formatHour` | `(h: number) => string` | locale default | Custom hour label formatter |
140
+
141
+ ### State
142
+
143
+ | Prop | Type | Default | Description |
144
+ |------|------|---------|-------------|
145
+ | `readOnly` | `boolean` | `false` | Display-only mode (no interactions) |
146
+ | `disabled` | `boolean` | `false` | Fully disabled state |
147
+ | `disabledSlots` | `Schedule` | — | Slots that cannot be selected |
148
+
149
+ ### Toolbar
150
+
151
+ | Prop | Type | Default | Description |
152
+ |------|------|---------|-------------|
153
+ | `presets` | `Preset[]` | 4 built-in | Preset buttons. Empty array hides the toolbar entries |
154
+ | `hideToolbar` | `boolean` | `false` | Hide the toolbar entirely |
155
+
156
+ ### Styling
157
+
158
+ | Prop | Type | Default | Description |
159
+ |------|------|---------|-------------|
160
+ | `className` | `string` | — | Extra class on the outermost container |
161
+
162
+ ## Localization Details
163
+
164
+ Each `locale` preset bundles week start, hour format, weekend colors, and translated text in one prop:
165
+
166
+ | `locale` | Week start | Hour format | Sat / Sun | `clear` |
167
+ |----------|-----------|-------------|-----------|---------|
168
+ | `"en"` | Mon | `14` | none | Clear |
169
+ | `"en-US"` | Sun | `2PM` | none | Clear |
170
+ | `"ko"` | Mon | `14` | blue / red | 초기화 |
171
+ | `"ja"` | Sun | `14` | blue / red | クリア |
172
+ | `"zh-CN"` | Mon | `14` | red / red | 清除 |
173
+ | `"zh-TW"` | Mon | `14` | red / red | 清除 |
174
+
175
+ Preset button labels (Weekday Day / Weekday Night / etc.) are translated per locale as well.
176
+
177
+ ### Override priority
178
+
179
+ ```
180
+ explicit prop (dayLabels / formatHour / visibleDays / presets)
181
+ > messages / weekStartsOn / weekendHighlight props
182
+ > LOCALE_PRESETS[locale]
183
+ > "en" fallback
184
+ ```
185
+
186
+ ## Serialization
187
+
188
+ The internal `Schedule` shape is optimized for rendering. For API transport and storage, convert it with one of the bundled helpers.
189
+
190
+ ### `toRanges(schedule, options?)`
191
+
192
+ Compress consecutive hours into `[start, end)` ranges, keyed by ISO 8601 day-of-week (1=Mon … 7=Sun).
193
+
194
+ ```ts
195
+ import { toRanges } from "react-schedule-picker";
196
+
197
+ toRanges({ mon: [9, 10, 11, 14, 15] }, { timezone: "Asia/Seoul" });
198
+ // {
199
+ // version: 1,
200
+ // timezone: "Asia/Seoul",
201
+ // ranges: [
202
+ // { day: 1, start: "09:00", end: "12:00" },
203
+ // { day: 1, start: "14:00", end: "16:00" },
204
+ // ],
205
+ // }
206
+ ```
207
+
208
+ ### `fromRanges(payload)`
209
+
210
+ Inverse of `toRanges`. Accepts only hour-aligned input.
211
+
212
+ ```ts
213
+ import { fromRanges } from "react-schedule-picker";
214
+
215
+ fromRanges({
216
+ version: 1,
217
+ ranges: [{ day: 1, start: "09:00", end: "12:00" }],
218
+ });
219
+ // { mon: [9, 10, 11] }
220
+ ```
221
+
222
+ ### `toISO(schedule, options?)`
223
+
224
+ Same data as `toRanges`, but with iCalendar-style field names (`startTime` / `endTime`).
225
+
226
+ ```ts
227
+ import { toISO } from "react-schedule-picker";
228
+
229
+ toISO({ mon: [9, 10, 11] }, { timezone: "Asia/Seoul" });
230
+ // {
231
+ // version: 1,
232
+ // timezone: "Asia/Seoul",
233
+ // availability: [
234
+ // { day: 1, startTime: "09:00", endTime: "12:00" },
235
+ // ],
236
+ // }
237
+ ```
238
+
239
+ ### Notes
240
+
241
+ - 1-hour granularity only. Sub-hour (15/30 min) is not supported.
242
+ - Ranges use half-open intervals: `end` is exclusive. `end: "24:00"` means up to midnight.
243
+ - Day keys in the raw `Schedule` are strings (`"mon"`…`"sun"`). ISO numeric keys appear only in serialized output.
244
+
245
+ ## Theming
246
+
247
+ All colors and sizes are CSS custom properties on `.rsp-container`. Override what you need:
248
+
249
+ ```css
250
+ .rsp-container {
251
+ --rsp-color-selected: #34d399;
252
+ --rsp-color-border: #d4d4d8;
253
+ --rsp-cell-size: 40px;
254
+ --rsp-border-radius: 12px;
255
+ }
256
+ ```
257
+
258
+ See [`src/variables.css`](./src/variables.css) for the full list.
259
+
260
+ ## Requirements
261
+
262
+ - React 18+
263
+ - Modern browsers (last 2 versions of Chrome, Safari, Firefox, Edge)
264
+ - TypeScript 4.7+ (optional)
265
+
266
+ ## License
267
+
268
+ [MIT](./LICENSE) © [innerbloo](https://github.com/innerbloo)
package/dist/index.css ADDED
@@ -0,0 +1,253 @@
1
+ /* src/variables.css */
2
+ :root {
3
+ --rsp-color-primary: #2d6af6;
4
+ --rsp-color-primary-light: #ecf1ff;
5
+ --rsp-color-selected: #bbcaff;
6
+ --rsp-color-selected-readonly: #d4d4d8;
7
+ --rsp-color-border: #e4e4e7;
8
+ --rsp-color-bg: #ffffff;
9
+ --rsp-color-bg-header: #f4f4f5;
10
+ --rsp-color-bg-label: #fafafa;
11
+ --rsp-color-bg-hover: #efeff1;
12
+ --rsp-color-text: #2c2c31;
13
+ --rsp-color-text-sub: #71717a;
14
+ --rsp-color-text-header: #1c1c20;
15
+ --rsp-color-saturday: var(--rsp-color-primary);
16
+ --rsp-color-sunday: #f04646;
17
+ --rsp-cell-size: 36px;
18
+ --rsp-day-col-width: 48px;
19
+ --rsp-border-radius: 8px;
20
+ --rsp-font-family: inherit;
21
+ --rsp-font-size-sm: 12px;
22
+ --rsp-font-size-md: 14px;
23
+ --rsp-preset-bg: var(--rsp-color-bg);
24
+ --rsp-preset-bg-hover: var(--rsp-color-bg-hover);
25
+ --rsp-preset-border: var(--rsp-color-border);
26
+ --rsp-preset-text: var(--rsp-color-text);
27
+ --rsp-preset-radius: 9999px;
28
+ }
29
+
30
+ /* src/SchedulePicker.css */
31
+ .rsp-container {
32
+ display: flex;
33
+ flex-direction: column;
34
+ gap: 8px;
35
+ font-family: var(--rsp-font-family);
36
+ }
37
+ .rsp-toolbar {
38
+ display: flex;
39
+ align-items: center;
40
+ gap: 8px;
41
+ flex-wrap: wrap;
42
+ }
43
+ .rsp-toolbar-clear {
44
+ margin-left: auto;
45
+ }
46
+ .rsp-preset-button {
47
+ font-size: var(--rsp-font-size-md);
48
+ line-height: 1.6;
49
+ padding: 2px 8px;
50
+ border: 1px solid var(--rsp-preset-border);
51
+ border-radius: var(--rsp-preset-radius);
52
+ background-color: var(--rsp-preset-bg);
53
+ color: var(--rsp-preset-text);
54
+ cursor: pointer;
55
+ white-space: nowrap;
56
+ }
57
+ .rsp-preset-button:hover {
58
+ background-color: var(--rsp-preset-bg-hover);
59
+ }
60
+ .rsp-table-wrapper {
61
+ overflow-x: auto;
62
+ -webkit-overflow-scrolling: touch;
63
+ user-select: none;
64
+ -webkit-user-select: none;
65
+ }
66
+ .rsp-table {
67
+ border-collapse: separate;
68
+ border-spacing: 0;
69
+ border: 1px solid var(--rsp-color-border);
70
+ border-radius: var(--rsp-border-radius);
71
+ width: 100%;
72
+ overflow: hidden;
73
+ background-color: var(--rsp-color-bg);
74
+ }
75
+ .rsp-day-col {
76
+ width: var(--rsp-day-col-width);
77
+ }
78
+ .rsp-header-row {
79
+ background-color: var(--rsp-color-bg-header);
80
+ }
81
+ .rsp-corner-cell {
82
+ background-color: var(--rsp-color-bg-header);
83
+ }
84
+ .rsp-corner-cell--clickable {
85
+ cursor: pointer;
86
+ }
87
+ .rsp-corner-cell--clickable:hover {
88
+ background-color: var(--rsp-color-bg-hover);
89
+ }
90
+ .rsp-header-cell {
91
+ font-size: var(--rsp-font-size-sm);
92
+ line-height: 1.6;
93
+ font-weight: 400;
94
+ color: var(--rsp-color-text-header);
95
+ text-align: center;
96
+ padding: 6px 0;
97
+ cursor: pointer;
98
+ touch-action: none;
99
+ }
100
+ .rsp-header-cell span {
101
+ display: inline-block;
102
+ }
103
+ .rsp-header-cell:hover {
104
+ background-color: var(--rsp-color-bg-hover);
105
+ }
106
+ .rsp-day-row {
107
+ }
108
+ .rsp-day-label {
109
+ font-size: var(--rsp-font-size-sm);
110
+ line-height: 1;
111
+ font-weight: 400;
112
+ color: var(--rsp-color-text-header);
113
+ text-align: center;
114
+ height: var(--rsp-cell-size);
115
+ padding: 0;
116
+ background-color: var(--rsp-color-bg-label);
117
+ border-top: 1px solid var(--rsp-color-border);
118
+ cursor: pointer;
119
+ touch-action: none;
120
+ }
121
+ .rsp-day-label:hover {
122
+ background-color: var(--rsp-color-bg-hover);
123
+ }
124
+ .rsp-cell {
125
+ padding: 0;
126
+ width: var(--rsp-cell-size);
127
+ height: var(--rsp-cell-size);
128
+ border-left: 1px solid var(--rsp-color-border);
129
+ border-top: 1px solid var(--rsp-color-border);
130
+ cursor: pointer;
131
+ touch-action: none;
132
+ -webkit-touch-callout: none;
133
+ }
134
+ .rsp-cell:hover {
135
+ background-color: var(--rsp-color-bg-hover);
136
+ }
137
+ .rsp-cell--highlighted {
138
+ background-color: var(--rsp-color-bg-hover);
139
+ }
140
+ .rsp-cell--selected {
141
+ background-color: var(--rsp-color-selected);
142
+ }
143
+ .rsp-cell--selected:hover {
144
+ background-color: var(--rsp-color-selected);
145
+ filter: brightness(1.1);
146
+ }
147
+ .rsp-cell--disabled {
148
+ background-color: var(--rsp-color-bg-header);
149
+ cursor: not-allowed;
150
+ background-image:
151
+ repeating-linear-gradient(
152
+ 45deg,
153
+ transparent,
154
+ transparent 3px,
155
+ var(--rsp-color-border) 3px,
156
+ var(--rsp-color-border) 4px);
157
+ }
158
+ .rsp-cell--disabled:hover {
159
+ background-color: var(--rsp-color-bg-header);
160
+ background-image:
161
+ repeating-linear-gradient(
162
+ 45deg,
163
+ transparent,
164
+ transparent 3px,
165
+ var(--rsp-color-border) 3px,
166
+ var(--rsp-color-border) 4px);
167
+ }
168
+ .rsp-cell--readonly {
169
+ cursor: default;
170
+ }
171
+ .rsp-cell--readonly:hover {
172
+ background-color: transparent;
173
+ }
174
+ .rsp-cell--readonly.rsp-cell--selected {
175
+ background-color: var(--rsp-color-selected-readonly);
176
+ }
177
+ .rsp-cell--readonly.rsp-cell--selected:hover {
178
+ background-color: var(--rsp-color-selected-readonly);
179
+ }
180
+ .rsp-day-label--readonly,
181
+ .rsp-header-cell--readonly {
182
+ cursor: default;
183
+ }
184
+ .rsp-day-label--readonly:hover {
185
+ background-color: var(--rsp-color-bg-label);
186
+ }
187
+ .rsp-header-cell--readonly:hover {
188
+ background-color: transparent;
189
+ }
190
+ .rsp-container--disabled {
191
+ pointer-events: none;
192
+ }
193
+ .rsp-container--disabled .rsp-cell {
194
+ background-color: var(--rsp-color-bg-header);
195
+ }
196
+ .rsp-container--disabled .rsp-cell--selected {
197
+ background-color: var(--rsp-color-selected-readonly);
198
+ }
199
+ .rsp-container--disabled .rsp-header-cell,
200
+ .rsp-container--disabled .rsp-day-label,
201
+ .rsp-container--disabled .rsp-hour-label {
202
+ color: var(--rsp-color-text-sub);
203
+ }
204
+ .rsp-container--disabled .rsp-preset-button {
205
+ color: var(--rsp-color-text-sub);
206
+ border-color: var(--rsp-color-border);
207
+ }
208
+ .rsp-hour-col {
209
+ width: var(--rsp-day-col-width);
210
+ }
211
+ .rsp-hour-label {
212
+ font-size: var(--rsp-font-size-sm);
213
+ line-height: 1;
214
+ font-weight: 400;
215
+ color: var(--rsp-color-text-header);
216
+ text-align: center;
217
+ height: var(--rsp-cell-size);
218
+ padding: 0;
219
+ background-color: var(--rsp-color-bg-label);
220
+ border-top: 1px solid var(--rsp-color-border);
221
+ cursor: pointer;
222
+ touch-action: none;
223
+ }
224
+ .rsp-hour-label:hover {
225
+ background-color: var(--rsp-color-bg-hover);
226
+ }
227
+ .rsp-hour-label--readonly {
228
+ cursor: default;
229
+ }
230
+ .rsp-hour-label--readonly:hover {
231
+ background-color: var(--rsp-color-bg-label);
232
+ }
233
+ .rsp-header-cell--day {
234
+ text-align: center;
235
+ }
236
+ .rsp-header-cell--day span {
237
+ transform: none;
238
+ }
239
+ @media (max-width: 639px) {
240
+ .rsp-day-label {
241
+ width: 36px;
242
+ font-size: var(--rsp-font-size-sm);
243
+ padding: 6px 0;
244
+ }
245
+ .rsp-day-col {
246
+ width: 36px;
247
+ }
248
+ .rsp-cell {
249
+ width: 30px;
250
+ height: 30px;
251
+ }
252
+ }
253
+ /*# sourceMappingURL=index.css.map */
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/variables.css","../src/SchedulePicker.css"],"sourcesContent":[":root {\n /* 색상 */\n --rsp-color-primary: #2d6af6;\n --rsp-color-primary-light: #ecf1ff;\n --rsp-color-selected: #bbcaff;\n --rsp-color-selected-readonly: #d4d4d8;\n --rsp-color-border: #e4e4e7;\n --rsp-color-bg: #ffffff;\n --rsp-color-bg-header: #f4f4f5;\n --rsp-color-bg-label: #fafafa;\n --rsp-color-bg-hover: #efeff1;\n --rsp-color-text: #2c2c31;\n --rsp-color-text-sub: #71717a;\n --rsp-color-text-header: #1c1c20;\n --rsp-color-saturday: var(--rsp-color-primary);\n --rsp-color-sunday: #f04646;\n\n /* 크기 */\n --rsp-cell-size: 36px;\n --rsp-day-col-width: 48px;\n --rsp-border-radius: 8px;\n\n /* 폰트 */\n --rsp-font-family: inherit;\n --rsp-font-size-sm: 12px;\n --rsp-font-size-md: 14px;\n\n /* 프리셋 버튼 */\n --rsp-preset-bg: var(--rsp-color-bg);\n --rsp-preset-bg-hover: var(--rsp-color-bg-hover);\n --rsp-preset-border: var(--rsp-color-border);\n --rsp-preset-text: var(--rsp-color-text);\n --rsp-preset-radius: 9999px;\n}\n\n","@import \"./variables.css\";\n\n/* 컨테이너 */\n.rsp-container {\n display: flex;\n flex-direction: column;\n gap: 8px;\n font-family: var(--rsp-font-family);\n}\n\n/* 프리셋 툴바 */\n.rsp-toolbar {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.rsp-toolbar-clear {\n margin-left: auto;\n}\n\n.rsp-preset-button {\n font-size: var(--rsp-font-size-md);\n line-height: 1.6;\n padding: 2px 8px;\n border: 1px solid var(--rsp-preset-border);\n border-radius: var(--rsp-preset-radius);\n background-color: var(--rsp-preset-bg);\n color: var(--rsp-preset-text);\n cursor: pointer;\n white-space: nowrap;\n}\n\n.rsp-preset-button:hover {\n background-color: var(--rsp-preset-bg-hover);\n}\n\n/* 테이블 래퍼 — 가로 스크롤 */\n.rsp-table-wrapper {\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n user-select: none;\n -webkit-user-select: none;\n}\n\n/* 테이블 */\n.rsp-table {\n border-collapse: separate;\n border-spacing: 0;\n border: 1px solid var(--rsp-color-border);\n border-radius: var(--rsp-border-radius);\n width: 100%;\n overflow: hidden;\n background-color: var(--rsp-color-bg);\n}\n\n/* 요일 라벨 컬럼 폭 */\n.rsp-day-col {\n width: var(--rsp-day-col-width);\n}\n\n/* 헤더 행 */\n.rsp-header-row {\n background-color: var(--rsp-color-bg-header);\n}\n\n/* 좌상단 코너 셀 */\n.rsp-corner-cell {\n background-color: var(--rsp-color-bg-header);\n}\n\n.rsp-corner-cell--clickable {\n cursor: pointer;\n}\n\n.rsp-corner-cell--clickable:hover {\n background-color: var(--rsp-color-bg-hover);\n}\n\n/* 시간 헤더 셀 */\n.rsp-header-cell {\n font-size: var(--rsp-font-size-sm);\n line-height: 1.6;\n font-weight: 400;\n color: var(--rsp-color-text-header);\n text-align: center;\n padding: 6px 0;\n cursor: pointer;\n touch-action: none;\n}\n\n.rsp-header-cell span {\n display: inline-block;\n}\n\n.rsp-header-cell:hover {\n background-color: var(--rsp-color-bg-hover);\n}\n\n/* 요일 행 */\n.rsp-day-row {\n}\n\n/* 요일 라벨 셀 */\n.rsp-day-label {\n font-size: var(--rsp-font-size-sm);\n line-height: 1;\n font-weight: 400;\n color: var(--rsp-color-text-header);\n text-align: center;\n height: var(--rsp-cell-size);\n padding: 0;\n background-color: var(--rsp-color-bg-label);\n border-top: 1px solid var(--rsp-color-border);\n cursor: pointer;\n touch-action: none;\n}\n\n.rsp-day-label:hover {\n background-color: var(--rsp-color-bg-hover);\n}\n\n/* 주말 강조 색상은 컴포넌트가 weekendHighlight prop/locale 기반 인라인 style로 주입합니다.\n .rsp-day-label--sat, .rsp-day-label--sun 클래스명은 사용자 커스텀 CSS 오버라이드를 위해 유지합니다. */\n\n/* 시간 셀 */\n.rsp-cell {\n padding: 0;\n width: var(--rsp-cell-size);\n height: var(--rsp-cell-size);\n border-left: 1px solid var(--rsp-color-border);\n border-top: 1px solid var(--rsp-color-border);\n cursor: pointer;\n touch-action: none;\n -webkit-touch-callout: none;\n}\n\n.rsp-cell:hover {\n background-color: var(--rsp-color-bg-hover);\n}\n\n.rsp-cell--highlighted {\n background-color: var(--rsp-color-bg-hover);\n}\n\n.rsp-cell--selected {\n background-color: var(--rsp-color-selected);\n}\n\n.rsp-cell--selected:hover {\n background-color: var(--rsp-color-selected);\n filter: brightness(1.1);\n}\n\n/* disabledSlots: 빗금 패턴 */\n.rsp-cell--disabled {\n background-color: var(--rsp-color-bg-header);\n cursor: not-allowed;\n background-image: repeating-linear-gradient(\n 45deg,\n transparent,\n transparent 3px,\n var(--rsp-color-border) 3px,\n var(--rsp-color-border) 4px\n );\n}\n\n.rsp-cell--disabled:hover {\n background-color: var(--rsp-color-bg-header);\n background-image: repeating-linear-gradient(\n 45deg,\n transparent,\n transparent 3px,\n var(--rsp-color-border) 3px,\n var(--rsp-color-border) 4px\n );\n}\n\n/* readOnly: 선택 셀 회색, 호버 없음 */\n.rsp-cell--readonly {\n cursor: default;\n}\n\n.rsp-cell--readonly:hover {\n background-color: transparent;\n}\n\n.rsp-cell--readonly.rsp-cell--selected {\n background-color: var(--rsp-color-selected-readonly);\n}\n\n.rsp-cell--readonly.rsp-cell--selected:hover {\n background-color: var(--rsp-color-selected-readonly);\n}\n\n.rsp-day-label--readonly,\n.rsp-header-cell--readonly {\n cursor: default;\n}\n\n.rsp-day-label--readonly:hover {\n background-color: var(--rsp-color-bg-label);\n}\n\n.rsp-header-cell--readonly:hover {\n background-color: transparent;\n}\n\n/* disabled: 전체 회색 배경 + 상호작용 불가 */\n.rsp-container--disabled {\n pointer-events: none;\n}\n\n.rsp-container--disabled .rsp-cell {\n background-color: var(--rsp-color-bg-header);\n}\n\n.rsp-container--disabled .rsp-cell--selected {\n background-color: var(--rsp-color-selected-readonly);\n}\n\n.rsp-container--disabled .rsp-header-cell,\n.rsp-container--disabled .rsp-day-label,\n.rsp-container--disabled .rsp-hour-label {\n color: var(--rsp-color-text-sub);\n}\n\n.rsp-container--disabled .rsp-preset-button {\n color: var(--rsp-color-text-sub);\n border-color: var(--rsp-color-border);\n}\n\n/* dayAxis=\"x\" 모드: 시간 라벨 컬럼 */\n.rsp-hour-col {\n width: var(--rsp-day-col-width);\n}\n\n.rsp-hour-label {\n font-size: var(--rsp-font-size-sm);\n line-height: 1;\n font-weight: 400;\n color: var(--rsp-color-text-header);\n text-align: center;\n height: var(--rsp-cell-size);\n padding: 0;\n background-color: var(--rsp-color-bg-label);\n border-top: 1px solid var(--rsp-color-border);\n cursor: pointer;\n touch-action: none;\n}\n\n.rsp-hour-label:hover {\n background-color: var(--rsp-color-bg-hover);\n}\n\n.rsp-hour-label--readonly {\n cursor: default;\n}\n\n.rsp-hour-label--readonly:hover {\n background-color: var(--rsp-color-bg-label);\n}\n\n/* dayAxis=\"x\" 모드: 요일 헤더 센터 정렬 */\n.rsp-header-cell--day {\n text-align: center;\n}\n\n.rsp-header-cell--day span {\n transform: none;\n}\n\n/* 모바일 최적화 (터치 타겟 권장 크기 ~30px+) */\n@media (max-width: 639px) {\n .rsp-day-label {\n width: 36px;\n font-size: var(--rsp-font-size-sm);\n padding: 6px 0;\n }\n\n .rsp-day-col {\n width: 36px;\n }\n\n .rsp-cell {\n width: 30px;\n height: 30px;\n }\n}\n"],"mappings":"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","names":[]}