@papernote/ui 1.10.4 → 1.10.5
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/dist/components/DataTable.d.ts.map +1 -1
- package/dist/components/TimezoneSelector.d.ts +102 -0
- package/dist/components/TimezoneSelector.d.ts.map +1 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/index.d.ts +101 -2
- package/dist/index.esm.js +286 -3
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +288 -2
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/components/DataTable.tsx +7 -3
- package/src/components/TimezoneSelector.stories.tsx +314 -0
- package/src/components/TimezoneSelector.tsx +393 -0
- package/src/components/index.ts +3 -0
- package/src/styles/index.css +1 -1
package/dist/index.js
CHANGED
|
@@ -2181,6 +2181,286 @@ const validationMessageColors = {
|
|
|
2181
2181
|
warning: 'text-warning-600',
|
|
2182
2182
|
};
|
|
2183
2183
|
|
|
2184
|
+
/**
|
|
2185
|
+
* Get the current UTC offset for a timezone
|
|
2186
|
+
*/
|
|
2187
|
+
function getTimezoneOffset(timezone) {
|
|
2188
|
+
try {
|
|
2189
|
+
const now = new Date();
|
|
2190
|
+
const formatter = new Intl.DateTimeFormat('en-US', {
|
|
2191
|
+
timeZone: timezone,
|
|
2192
|
+
timeZoneName: 'longOffset',
|
|
2193
|
+
});
|
|
2194
|
+
const parts = formatter.formatToParts(now);
|
|
2195
|
+
const tzPart = parts.find(p => p.type === 'timeZoneName');
|
|
2196
|
+
if (tzPart?.value) {
|
|
2197
|
+
// Extract offset from format like "GMT-05:00" or "GMT+05:30"
|
|
2198
|
+
const match = tzPart.value.match(/GMT([+-]\d{2}:\d{2})/);
|
|
2199
|
+
if (match) {
|
|
2200
|
+
const offsetStr = match[1];
|
|
2201
|
+
const [hours, minutes] = offsetStr.split(':').map(Number);
|
|
2202
|
+
const sign = offsetStr.startsWith('-') ? -1 : 1;
|
|
2203
|
+
const totalMinutes = sign * (Math.abs(hours) * 60 + minutes);
|
|
2204
|
+
return {
|
|
2205
|
+
offset: `UTC${offsetStr}`,
|
|
2206
|
+
offsetMinutes: totalMinutes,
|
|
2207
|
+
};
|
|
2208
|
+
}
|
|
2209
|
+
// Handle "GMT" (UTC+00:00)
|
|
2210
|
+
if (tzPart.value === 'GMT') {
|
|
2211
|
+
return { offset: 'UTC+00:00', offsetMinutes: 0 };
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
// Fallback: calculate manually
|
|
2215
|
+
const utcDate = new Date(now.toLocaleString('en-US', { timeZone: 'UTC' }));
|
|
2216
|
+
const tzDate = new Date(now.toLocaleString('en-US', { timeZone: timezone }));
|
|
2217
|
+
const diffMinutes = (tzDate.getTime() - utcDate.getTime()) / 60000;
|
|
2218
|
+
const hours = Math.floor(Math.abs(diffMinutes) / 60);
|
|
2219
|
+
const mins = Math.abs(diffMinutes) % 60;
|
|
2220
|
+
const sign = diffMinutes >= 0 ? '+' : '-';
|
|
2221
|
+
const offset = `UTC${sign}${String(hours).padStart(2, '0')}:${String(mins).padStart(2, '0')}`;
|
|
2222
|
+
return { offset, offsetMinutes: diffMinutes };
|
|
2223
|
+
}
|
|
2224
|
+
catch {
|
|
2225
|
+
return { offset: 'UTC+00:00', offsetMinutes: 0 };
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
/**
|
|
2229
|
+
* Comprehensive list of common timezones organized by region
|
|
2230
|
+
*/
|
|
2231
|
+
const TIMEZONE_DATA = {
|
|
2232
|
+
Africa: [
|
|
2233
|
+
'Africa/Cairo',
|
|
2234
|
+
'Africa/Casablanca',
|
|
2235
|
+
'Africa/Johannesburg',
|
|
2236
|
+
'Africa/Lagos',
|
|
2237
|
+
'Africa/Nairobi',
|
|
2238
|
+
'Africa/Tunis',
|
|
2239
|
+
],
|
|
2240
|
+
America: [
|
|
2241
|
+
'America/Anchorage',
|
|
2242
|
+
'America/Argentina/Buenos_Aires',
|
|
2243
|
+
'America/Bogota',
|
|
2244
|
+
'America/Caracas',
|
|
2245
|
+
'America/Chicago',
|
|
2246
|
+
'America/Denver',
|
|
2247
|
+
'America/Halifax',
|
|
2248
|
+
'America/Lima',
|
|
2249
|
+
'America/Los_Angeles',
|
|
2250
|
+
'America/Mexico_City',
|
|
2251
|
+
'America/New_York',
|
|
2252
|
+
'America/Phoenix',
|
|
2253
|
+
'America/Santiago',
|
|
2254
|
+
'America/Sao_Paulo',
|
|
2255
|
+
'America/Toronto',
|
|
2256
|
+
'America/Vancouver',
|
|
2257
|
+
],
|
|
2258
|
+
Antarctica: [
|
|
2259
|
+
'Antarctica/McMurdo',
|
|
2260
|
+
'Antarctica/Palmer',
|
|
2261
|
+
'Antarctica/Syowa',
|
|
2262
|
+
],
|
|
2263
|
+
Asia: [
|
|
2264
|
+
'Asia/Bangkok',
|
|
2265
|
+
'Asia/Colombo',
|
|
2266
|
+
'Asia/Dhaka',
|
|
2267
|
+
'Asia/Dubai',
|
|
2268
|
+
'Asia/Hong_Kong',
|
|
2269
|
+
'Asia/Jakarta',
|
|
2270
|
+
'Asia/Jerusalem',
|
|
2271
|
+
'Asia/Karachi',
|
|
2272
|
+
'Asia/Kathmandu',
|
|
2273
|
+
'Asia/Kolkata',
|
|
2274
|
+
'Asia/Kuala_Lumpur',
|
|
2275
|
+
'Asia/Manila',
|
|
2276
|
+
'Asia/Qatar',
|
|
2277
|
+
'Asia/Riyadh',
|
|
2278
|
+
'Asia/Seoul',
|
|
2279
|
+
'Asia/Shanghai',
|
|
2280
|
+
'Asia/Singapore',
|
|
2281
|
+
'Asia/Taipei',
|
|
2282
|
+
'Asia/Tehran',
|
|
2283
|
+
'Asia/Tokyo',
|
|
2284
|
+
],
|
|
2285
|
+
Atlantic: [
|
|
2286
|
+
'Atlantic/Azores',
|
|
2287
|
+
'Atlantic/Bermuda',
|
|
2288
|
+
'Atlantic/Canary',
|
|
2289
|
+
'Atlantic/Cape_Verde',
|
|
2290
|
+
'Atlantic/Reykjavik',
|
|
2291
|
+
],
|
|
2292
|
+
Australia: [
|
|
2293
|
+
'Australia/Adelaide',
|
|
2294
|
+
'Australia/Brisbane',
|
|
2295
|
+
'Australia/Darwin',
|
|
2296
|
+
'Australia/Hobart',
|
|
2297
|
+
'Australia/Melbourne',
|
|
2298
|
+
'Australia/Perth',
|
|
2299
|
+
'Australia/Sydney',
|
|
2300
|
+
],
|
|
2301
|
+
Europe: [
|
|
2302
|
+
'Europe/Amsterdam',
|
|
2303
|
+
'Europe/Athens',
|
|
2304
|
+
'Europe/Berlin',
|
|
2305
|
+
'Europe/Brussels',
|
|
2306
|
+
'Europe/Bucharest',
|
|
2307
|
+
'Europe/Budapest',
|
|
2308
|
+
'Europe/Copenhagen',
|
|
2309
|
+
'Europe/Dublin',
|
|
2310
|
+
'Europe/Helsinki',
|
|
2311
|
+
'Europe/Istanbul',
|
|
2312
|
+
'Europe/Lisbon',
|
|
2313
|
+
'Europe/London',
|
|
2314
|
+
'Europe/Madrid',
|
|
2315
|
+
'Europe/Moscow',
|
|
2316
|
+
'Europe/Oslo',
|
|
2317
|
+
'Europe/Paris',
|
|
2318
|
+
'Europe/Prague',
|
|
2319
|
+
'Europe/Rome',
|
|
2320
|
+
'Europe/Stockholm',
|
|
2321
|
+
'Europe/Vienna',
|
|
2322
|
+
'Europe/Warsaw',
|
|
2323
|
+
'Europe/Zurich',
|
|
2324
|
+
],
|
|
2325
|
+
Indian: [
|
|
2326
|
+
'Indian/Maldives',
|
|
2327
|
+
'Indian/Mauritius',
|
|
2328
|
+
],
|
|
2329
|
+
Pacific: [
|
|
2330
|
+
'Pacific/Auckland',
|
|
2331
|
+
'Pacific/Fiji',
|
|
2332
|
+
'Pacific/Guam',
|
|
2333
|
+
'Pacific/Honolulu',
|
|
2334
|
+
'Pacific/Noumea',
|
|
2335
|
+
'Pacific/Pago_Pago',
|
|
2336
|
+
'Pacific/Tahiti',
|
|
2337
|
+
],
|
|
2338
|
+
};
|
|
2339
|
+
/**
|
|
2340
|
+
* Region display names
|
|
2341
|
+
*/
|
|
2342
|
+
const REGION_LABELS = {
|
|
2343
|
+
Africa: 'Africa',
|
|
2344
|
+
America: 'Americas',
|
|
2345
|
+
Antarctica: 'Antarctica',
|
|
2346
|
+
Asia: 'Asia',
|
|
2347
|
+
Atlantic: 'Atlantic',
|
|
2348
|
+
Australia: 'Australia',
|
|
2349
|
+
Europe: 'Europe',
|
|
2350
|
+
Indian: 'Indian Ocean',
|
|
2351
|
+
Pacific: 'Pacific',
|
|
2352
|
+
};
|
|
2353
|
+
/**
|
|
2354
|
+
* Get the user's local timezone
|
|
2355
|
+
*/
|
|
2356
|
+
function getLocalTimezone() {
|
|
2357
|
+
try {
|
|
2358
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
2359
|
+
}
|
|
2360
|
+
catch {
|
|
2361
|
+
return 'UTC';
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
/**
|
|
2365
|
+
* Check if a timezone string is valid
|
|
2366
|
+
*/
|
|
2367
|
+
function isValidTimezone(timezone) {
|
|
2368
|
+
try {
|
|
2369
|
+
Intl.DateTimeFormat(undefined, { timeZone: timezone });
|
|
2370
|
+
return true;
|
|
2371
|
+
}
|
|
2372
|
+
catch {
|
|
2373
|
+
return false;
|
|
2374
|
+
}
|
|
2375
|
+
}
|
|
2376
|
+
/**
|
|
2377
|
+
* TimezoneSelector - A searchable dropdown for selecting timezones
|
|
2378
|
+
*
|
|
2379
|
+
* Provides a user-friendly way to select from IANA timezones, organized by
|
|
2380
|
+
* geographic region with UTC offset display.
|
|
2381
|
+
*
|
|
2382
|
+
* @example Basic usage
|
|
2383
|
+
* ```tsx
|
|
2384
|
+
* const [timezone, setTimezone] = useState('America/New_York');
|
|
2385
|
+
*
|
|
2386
|
+
* <TimezoneSelector
|
|
2387
|
+
* label="Select Timezone"
|
|
2388
|
+
* value={timezone}
|
|
2389
|
+
* onChange={setTimezone}
|
|
2390
|
+
* />
|
|
2391
|
+
* ```
|
|
2392
|
+
*
|
|
2393
|
+
* @example With local timezone detection
|
|
2394
|
+
* ```tsx
|
|
2395
|
+
* import { TimezoneSelector, getLocalTimezone } from 'notebook-ui';
|
|
2396
|
+
*
|
|
2397
|
+
* const [timezone, setTimezone] = useState(getLocalTimezone());
|
|
2398
|
+
*
|
|
2399
|
+
* <TimezoneSelector
|
|
2400
|
+
* value={timezone}
|
|
2401
|
+
* onChange={setTimezone}
|
|
2402
|
+
* includeUTC
|
|
2403
|
+
* clearable
|
|
2404
|
+
* />
|
|
2405
|
+
* ```
|
|
2406
|
+
*
|
|
2407
|
+
* @example Filter to specific regions
|
|
2408
|
+
* ```tsx
|
|
2409
|
+
* <TimezoneSelector
|
|
2410
|
+
* value={timezone}
|
|
2411
|
+
* onChange={setTimezone}
|
|
2412
|
+
* regions={['America', 'Europe']}
|
|
2413
|
+
* placeholder="Select US or European timezone..."
|
|
2414
|
+
* />
|
|
2415
|
+
* ```
|
|
2416
|
+
*/
|
|
2417
|
+
const TimezoneSelector = React.forwardRef(({ value, onChange, label, helperText, error, disabled = false, placeholder = 'Select timezone...', clearable = false, loading = false, size = 'md', includeUTC = true, regions, showOffset = true, }, ref) => {
|
|
2418
|
+
// Build timezone options grouped by region
|
|
2419
|
+
const groups = React.useMemo(() => {
|
|
2420
|
+
const result = [];
|
|
2421
|
+
// Add UTC as a special option at the top
|
|
2422
|
+
if (includeUTC) {
|
|
2423
|
+
result.push({
|
|
2424
|
+
label: 'Universal',
|
|
2425
|
+
options: [
|
|
2426
|
+
{
|
|
2427
|
+
value: 'UTC',
|
|
2428
|
+
label: showOffset ? 'UTC (UTC+00:00)' : 'UTC',
|
|
2429
|
+
},
|
|
2430
|
+
],
|
|
2431
|
+
});
|
|
2432
|
+
}
|
|
2433
|
+
// Determine which regions to include
|
|
2434
|
+
const activeRegions = regions || Object.keys(TIMEZONE_DATA);
|
|
2435
|
+
// Build groups for each region
|
|
2436
|
+
for (const region of activeRegions) {
|
|
2437
|
+
const timezones = TIMEZONE_DATA[region];
|
|
2438
|
+
if (!timezones)
|
|
2439
|
+
continue;
|
|
2440
|
+
// Build options with offset info and sort by offset
|
|
2441
|
+
const options = timezones
|
|
2442
|
+
.map(tz => {
|
|
2443
|
+
const { offset, offsetMinutes } = getTimezoneOffset(tz);
|
|
2444
|
+
const city = tz.split('/').pop()?.replace(/_/g, ' ') || tz;
|
|
2445
|
+
return {
|
|
2446
|
+
value: tz,
|
|
2447
|
+
label: showOffset ? `${city} (${offset})` : city,
|
|
2448
|
+
offsetMinutes,
|
|
2449
|
+
};
|
|
2450
|
+
})
|
|
2451
|
+
.sort((a, b) => a.offsetMinutes - b.offsetMinutes)
|
|
2452
|
+
.map(({ value, label }) => ({ value, label }));
|
|
2453
|
+
result.push({
|
|
2454
|
+
label: REGION_LABELS[region],
|
|
2455
|
+
options,
|
|
2456
|
+
});
|
|
2457
|
+
}
|
|
2458
|
+
return result;
|
|
2459
|
+
}, [includeUTC, regions, showOffset]);
|
|
2460
|
+
return (jsxRuntime.jsx(Select, { ref: ref, groups: groups, value: value, onChange: onChange, label: label, helperText: helperText, error: error, disabled: disabled, placeholder: placeholder, clearable: clearable, loading: loading, size: size, searchable: true, virtualized: false }));
|
|
2461
|
+
});
|
|
2462
|
+
TimezoneSelector.displayName = 'TimezoneSelector';
|
|
2463
|
+
|
|
2184
2464
|
/**
|
|
2185
2465
|
* Combobox component - searchable select with typeahead and custom value support.
|
|
2186
2466
|
*
|
|
@@ -11530,14 +11810,17 @@ mobileView = 'auto', cardConfig, cardGap = 'md', cardClassName, }) {
|
|
|
11530
11810
|
const columnKey = String(column.key);
|
|
11531
11811
|
const dynamicWidth = columnWidths[columnKey];
|
|
11532
11812
|
return (jsxRuntime.jsx("col", { style: getColumnStyle(column, dynamicWidth) }, index));
|
|
11533
|
-
})] }), jsxRuntime.jsx("thead", { className: `bg-paper-100 sticky top-0 z-10 ${headerClassName}`, children: jsxRuntime.jsxs("tr", { className: "table-header-row", children: [selectable && (jsxRuntime.jsx("th", { className: `sticky left-0 bg-paper-100 ${currentDensity.header} border-b ${borderColor} z-20 w-12 ${bordered ? `border ${borderColor}` : ''}`, children: jsxRuntime.jsx("input", { type: "checkbox", checked: selectedRowsSet.size === data.length && data.length > 0, onChange: handleSelectAll, className: "w-4 h-4 text-accent-600 border-paper-300 rounded focus:ring-accent-400", "aria-label": "Select all rows" }) })), ((expandable || expandedRowConfig) && showExpandChevron) && (jsxRuntime.jsx("th", { className: `sticky left-0 bg-paper-100 px-2 ${currentDensity.header} border-b ${borderColor} z-19 w-10 ${bordered ? `border ${borderColor}` : ''}` })), allActions.length > 0 && (jsxRuntime.jsx("th", { className: `sticky left-0 bg-paper-100
|
|
11813
|
+
})] }), jsxRuntime.jsx("thead", { className: `bg-paper-100 sticky top-0 z-10 ${headerClassName}`, children: jsxRuntime.jsxs("tr", { className: "table-header-row", children: [selectable && (jsxRuntime.jsx("th", { className: `sticky left-0 bg-paper-100 ${currentDensity.header} border-b ${borderColor} z-20 w-12 ${bordered ? `border ${borderColor}` : ''}`, children: jsxRuntime.jsx("input", { type: "checkbox", checked: selectedRowsSet.size === data.length && data.length > 0, onChange: handleSelectAll, className: "w-4 h-4 text-accent-600 border-paper-300 rounded focus:ring-accent-400", "aria-label": "Select all rows" }) })), ((expandable || expandedRowConfig) && showExpandChevron) && (jsxRuntime.jsx("th", { className: `sticky left-0 bg-paper-100 px-2 ${currentDensity.header} border-b ${borderColor} z-19 w-10 ${bordered ? `border ${borderColor}` : ''}` })), allActions.length > 0 && (jsxRuntime.jsx("th", { className: `sticky left-0 bg-paper-100 text-center text-xs font-medium text-ink-700 uppercase tracking-wider border-b ${borderColor} z-20 ${bordered ? `border ${borderColor}` : ''}`, style: { width: '28px', padding: '0' } })), visibleColumns.map((column, colIdx) => {
|
|
11534
11814
|
const columnKey = String(column.key);
|
|
11535
11815
|
const dynamicWidth = columnWidths[columnKey];
|
|
11536
11816
|
const thRef = React.useRef(null);
|
|
11537
11817
|
const isDragging = draggingColumn === columnKey;
|
|
11538
11818
|
const isDragOver = dragOverColumn === columnKey;
|
|
11819
|
+
// Reduce left padding on first column when there are action buttons (match body cells)
|
|
11820
|
+
const isFirstColumn = colIdx === 0;
|
|
11821
|
+
const headerPaddingClass = isFirstColumn && allActions.length > 0 ? 'pl-3' : '';
|
|
11539
11822
|
return (jsxRuntime.jsxs("th", { ref: thRef, draggable: reorderable, onDragStart: (e) => reorderable && handleDragStart(e, columnKey), onDragOver: (e) => reorderable && handleDragOver(e, columnKey), onDragEnd: handleDragEnd, onDrop: (e) => reorderable && handleDrop(e, columnKey), className: `
|
|
11540
|
-
${currentDensity.header} text-left border-b ${borderColor} ${bordered ? `border ${borderColor}` : ''} relative
|
|
11823
|
+
${currentDensity.header} ${headerPaddingClass} text-left border-b ${borderColor} ${bordered ? `border ${borderColor}` : ''} relative
|
|
11541
11824
|
${reorderable ? 'cursor-move' : ''}
|
|
11542
11825
|
${isDragging ? 'opacity-50' : ''}
|
|
11543
11826
|
${isDragOver ? 'bg-accent-100' : ''}
|
|
@@ -58377,6 +58660,7 @@ exports.Textarea = Textarea;
|
|
|
58377
58660
|
exports.ThemeToggle = ThemeToggle;
|
|
58378
58661
|
exports.TimePicker = TimePicker;
|
|
58379
58662
|
exports.Timeline = Timeline;
|
|
58663
|
+
exports.TimezoneSelector = TimezoneSelector;
|
|
58380
58664
|
exports.Toast = Toast;
|
|
58381
58665
|
exports.ToastContainer = ToastContainer;
|
|
58382
58666
|
exports.Tooltip = Tooltip;
|
|
@@ -58400,6 +58684,8 @@ exports.formatStatisticValue = formatStatisticValue;
|
|
|
58400
58684
|
exports.formatStatistics = formatStatistics;
|
|
58401
58685
|
exports.getFormula = getFormula;
|
|
58402
58686
|
exports.getFormulasByCategory = getFormulasByCategory;
|
|
58687
|
+
exports.getLocalTimezone = getLocalTimezone;
|
|
58688
|
+
exports.isValidTimezone = isValidTimezone;
|
|
58403
58689
|
exports.loadColumnOrder = loadColumnOrder;
|
|
58404
58690
|
exports.loadColumnWidths = loadColumnWidths;
|
|
58405
58691
|
exports.reorderArray = reorderArray;
|