@aiquants/spreadsheet 0.9.0 → 0.11.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/CHANGELOG.md +77 -0
- package/README.md +76 -5
- package/dist/index.cjs +14 -14
- package/dist/index.d.cts +166 -7
- package/dist/index.d.ts +166 -7
- package/dist/index.js +4378 -4184
- package/dist/styles/spreadsheet.standalone.css +1 -1
- package/package.json +5 -5
- package/src/Spreadsheet.tsx +210 -12
- package/src/clipboard/tiling.ts +106 -16
- package/src/clipboard/useClipboard.ts +120 -7
- package/src/commands/useCommands.ts +226 -42
- package/src/editor/useCellEditor.ts +62 -13
- package/src/grid/GridBody.tsx +51 -3
- package/src/index.ts +5 -1
- package/src/keyboard/useGridKeyboard.ts +24 -3
- package/src/model/dataAdapter.ts +121 -2
- package/src/model/labels.ts +8 -2
- package/src/overlay/useFillHandle.ts +35 -2
- package/src/popover/CommentPopover.tsx +14 -2
- package/src/popover/LinkPopover.tsx +15 -3
- package/src/popover/NotePopover.tsx +24 -6
- package/src/popover/usePopovers.ts +89 -11
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.com */
|
|
2
2
|
/*! @aiquants/spreadsheet standalone CSS — 非 Tailwind ホスト専用。ホストの Tailwind ビルドと混在させないこと */
|
|
3
|
-
@layer theme,base;@layer components{@layer components{.aqvs-scroll-pane{display:flex;position:relative}.aqvs-scroll-pane[data-self-measured=true]{height:100%}.aqvs-scroll-pane-content{box-sizing:border-box;flex:1;height:100%;position:relative;overflow:hidden}.aqvs-tap-scroll-circle{touch-action:none;-webkit-user-select:none;user-select:none;justify-content:center;align-items:center;transition-property:transform;transition-duration:.1s;transition-timing-function:cubic-bezier(0,0,.2,1);display:flex;position:relative}.aqvs-tap-scroll-circle-visual-outer{border-radius:9999px;position:absolute;inset:0}.aqvs-tap-scroll-circle-gradient{background:linear-gradient(to bottom right,#1d4ed899,#60a5fa8c,#bfdbfe66);border:1px solid #fff6;border-radius:9999px;position:absolute;inset:0;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.aqvs-tap-scroll-circle-inner{border-radius:9999px;position:absolute;inset:18%}.aqvs-sample-visual-pupil{box-sizing:border-box;background-color:#ffffffd9;border:1px solid #ffffff80;border-radius:9999px;position:absolute;top:50%;left:50%}.aqvs-sample-visual-highlight{background-color:#fffc;border-radius:9999px;position:absolute;top:50%;left:50%}.aqvs-sample-visual-rod{background-color:#ffffff80;border-radius:9999px;position:absolute;top:50%;left:50%}.aqvs-scrollbar{z-index:50;cursor:default;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;touch-action:none;background-color:#fff;position:relative}.aqvs-scrollbar-horizontal{flex-direction:row;align-items:stretch;display:flex}.aqvs-scrollbar-vertical{flex-direction:column;align-items:stretch;display:flex}.aqvs-scrollbar[data-visible=false],.aqvs-scrollbar-thumb-wrapper[data-visible=false]{opacity:0;pointer-events:none}.aqvs-scrollbar-tap-circle-wrapper{pointer-events:auto;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);position:absolute}.aqvs-scrollbar-arrow-button{color:#313131;letter-spacing:normal;background-color:#e0e0e0;border:0 solid;justify-content:center;align-items:center;padding:0;font-family:system-ui,sans-serif;font-size:.75rem;font-weight:400;line-height:1rem;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);display:flex}.aqvs-scrollbar-arrow-button:focus{outline-offset:2px;outline:2px solid #0000}.aqvs-scrollbar-arrow-button:focus-visible{outline-offset:1px;outline:2px solid #60a5fa}.aqvs-scrollbar-arrow-button:disabled{cursor:not-allowed;opacity:.5}.aqvs-scrollbar-arrow-button:enabled:hover{background-color:#d4d4d4}.aqvs-scrollbar-track{background-color:#f5f5f5;flex:1;position:relative}.aqvs-scrollbar-overlay{pointer-events:none;position:absolute;inset:0}.aqvs-scrollbar-thumb-wrapper{touch-action:none;position:absolute}.aqvs-scrollbar-thumb{transform-origin:50%;background-color:#7f7f7f;transition:transform 80ms ease-out;position:absolute}.aqvs-scrollbar-thumb[data-thumb-state=disabled]{transition:none;transform:none}.aqvs-scrollbar-thumb[data-thumb-state=hover]{background-color:#5f5f5f}.aqvs-scrollbar-thumb[data-thumb-state=dragging]{background-color:#4f4f4f;transition:transform 60ms ease-out}.aqvs-scrollbar-thumb-horizontal{inset:1.5px 0}.aqvs-scrollbar-thumb-horizontal[data-thumb-state=hover]{top:-.5px;bottom:-.5px;transform:scaleY(1.06)}.aqvs-scrollbar-thumb-horizontal[data-thumb-state=dragging]{top:-.5px;bottom:-.5px;transform:scaleY(1.12)}.aqvs-scrollbar-thumb-vertical{inset:0 1.5px}.aqvs-scrollbar-thumb-vertical[data-thumb-state=hover]{left:-.5px;right:-.5px;transform:scaleX(1.06)}.aqvs-scrollbar-thumb-vertical[data-thumb-state=dragging]{left:-.5px;right:-.5px;transform:scaleX(1.12)}.aqvs-scroll-to-edge-button{pointer-events:auto;cursor:pointer;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;color:#fff;text-transform:uppercase;letter-spacing:.05em;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);background-color:#1f2937cc;border:0 solid;border-radius:9999px;padding:.25rem 3rem;font-family:inherit;font-size:10px;font-weight:500;line-height:1.5;transition-property:transform,background-color;transition-duration:.15s;transform:none;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.aqvs-scroll-to-edge-button:focus{outline-offset:2px;outline:2px solid #0000}.aqvs-scroll-to-edge-button:focus-visible{outline-offset:1px;outline:2px solid #60a5fa}.aqvs-scroll-to-edge-button:hover{background-color:#374151}.aqvs-scroll-to-edge-button:active{transform:scale(.95)}.aqvs-scroll-to-edge-overlay{pointer-events:none;z-index:10;transition-property:opacity;transition-duration:.5s;position:absolute;inset:0}.aqvs-scroll-to-edge-overlay[data-visible=true]{opacity:1}.aqvs-scroll-to-edge-overlay[data-visible=false]{opacity:0}.aqvs-scroll-to-edge-overlay[data-visible=false] .aqvs-scroll-to-edge-button{pointer-events:none}.aqvs-scroll-to-edge-button-container{justify-content:center;display:flex;position:absolute;left:0;right:0}.aqvs-scroll-to-edge-button-container-top{top:.5rem}.aqvs-scroll-to-edge-button-container-bottom{bottom:.5rem}.aqvs-no-items-container{width:100%;position:absolute;top:0}.aqvs-no-items-text{text-align:center;color:#6b7280}.aqvs-item-container,.aqvs-bottom-inset,.aqvs-items-wrapper{width:100%;position:absolute}.aqvs-live-region{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.aqvs-grid{grid-template-rows:minmax(0,1fr) auto;width:100%;height:100%;display:grid;position:relative;overflow:hidden}.aqvs-grid-has-frozen-rows{grid-template-rows:auto minmax(0,1fr) auto}.aqvs-grid-has-trailing-rows{grid-template-rows:minmax(0,1fr) auto auto}.aqvs-grid-has-frozen-rows.aqvs-grid-has-trailing-rows{grid-template-rows:auto minmax(0,1fr) auto auto}.aqvs-grid-frozen-rows{min-width:0;position:relative;overflow:hidden}.aqvs-grid-frozen-band-row{position:absolute;left:0;overflow:hidden}.aqvs-grid-trailing-rows{min-width:0;position:relative;overflow:hidden}.aqvs-grid-trailing-rows-inner{position:absolute;bottom:0;left:0;right:0}.aqvs-grid-trailing-band-row{position:absolute;left:0;overflow:hidden}.aqvs-grid-main{min-width:0;min-height:0;position:relative}.aqvs-grid-row{width:100%;height:100%;transform:translateX(var(--aqvs-grid-hx-residual,0px));position:relative;overflow:visible}.aqvs-grid-cell{height:100%;position:absolute;top:0;overflow:hidden}.aqvs-grid-row.aqvs-grid-row-frozen-host{transform:none}.aqvs-grid-row-scroll{height:100%;top:0;left:var(--aqvs-grid-frozen-width,0px);right:var(--aqvs-grid-trailing-width,0px);position:absolute;overflow:hidden}.aqvs-grid-row-scroll-inner{width:100%;height:100%;transform:translateX(calc(var(--aqvs-grid-hx-residual,0px) - var(--aqvs-grid-frozen-width,0px)));position:absolute;top:0;left:0}.aqvs-grid-row-trailing{height:100%;top:0;left:max(var(--aqvs-grid-frozen-width,0px), calc(100% - var(--aqvs-grid-trailing-width,0px)));position:absolute;right:0;overflow:hidden}.aqvs-grid-row-trailing-inner{height:100%;width:var(--aqvs-grid-trailing-width,0px);position:absolute;top:0;right:0}.aqvs-grid-hbar-strip{min-width:0;padding-left:var(--aqvs-grid-frozen-width,0px);position:relative}.aqvs-grid-live-region{clip:rect(0 0 0 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}}.aqsp-root,.aqsp-root *,.aqsp-root :before,.aqsp-root :after{box-sizing:border-box}.aqsp-root{--aqsp-row-header-width:64px;--aqsp-col-header-height:24px;--aqsp-band-row:#0084ff;--aqsp-band-column:#42ff4f;--aqsp-band-intersection:#e1ff00;--aqsp-band-opacity:.2;color:#1f2937;background:#fff;width:100%;height:100%;font-size:13px;line-height:1;position:relative;overflow:hidden}.aqsp-zoom{grid-template-columns:var(--aqsp-row-header-width) minmax(0, 1fr);grid-template-rows:var(--aqsp-col-header-height) minmax(0, 1fr);transform-origin:0 0;width:100%;height:100%;display:grid}.aqsp-corner{background:#f3f4f6;border-bottom:1px solid #d1d5db;border-right:1px solid #d1d5db;grid-area:1/1}.aqsp-col-header{-webkit-user-select:none;user-select:none;background:#f3f4f6;border-bottom:1px solid #d1d5db;grid-area:1/2;position:relative;overflow:hidden}.aqsp-col-header-scroll{height:100%;position:absolute;top:0;right:0;overflow:hidden}.aqsp-row-header-scroll{width:100%;position:absolute;bottom:0;left:0;overflow:hidden}.aqsp-col-header-track{will-change:transform;position:absolute;inset:0}.aqsp-col-header-cell{white-space:nowrap;color:#4b5563;border-right:1px solid #e5e7eb;justify-content:center;align-items:center;height:100%;display:flex;position:absolute;top:0;overflow:hidden}.aqsp-row-header{-webkit-user-select:none;user-select:none;background:#f3f4f6;border-right:1px solid #d1d5db;grid-area:2/1;position:relative;overflow:hidden}.aqsp-row-header-track{will-change:transform;position:absolute;top:0;left:0;right:0}.aqsp-row-header-cell{white-space:nowrap;color:#4b5563;font-variant-numeric:tabular-nums;border-bottom:1px solid #e5e7eb;justify-content:flex-end;align-items:center;padding-right:6px;display:flex;position:absolute;left:0;right:0;overflow:hidden}.aqsp-body{position:absolute;inset:0}.aqsp-row{cursor:default}.aqsp-cell{white-space:nowrap;background:#fff;border-bottom:1px solid #e5e7eb;border-right:1px solid #e5e7eb;align-items:center;padding:0 4px;display:flex}.aqsp-cell[data-aqsp-valign=top]{align-items:flex-start}.aqsp-cell[data-aqsp-valign=bottom]{align-items:flex-end}.aqsp-cell[data-aqsp-wrap]{white-space:pre-wrap;overflow-wrap:anywhere}.aqsp-cell[data-aqsp-note]:after,.aqsp-cell[data-aqsp-comment]:after{content:"";pointer-events:none;border:5px solid #4b5563;border-color:#4b5563 #4b5563 #0000 #0000;width:0;height:0;position:absolute;top:0;right:0}.aqsp-cell[data-aqsp-comment]:after{border-top-color:#f97316;border-right-color:#f97316}.aqsp-cell[data-aqsp-invalid]:before{content:"";pointer-events:none;border:5px solid #0000;border-color:#0000 #0000 #dc2626 #dc2626;width:0;height:0;position:absolute;bottom:0;left:0}.aqsp-cell[data-aqsp-link]{color:#1d4ed8;text-decoration:underline}.aqsp-body-stack{grid-area:2/2;position:relative}.aqsp-overlay{pointer-events:none;position:absolute;inset:0;overflow:hidden}.aqsp-overlay-quad{pointer-events:none;position:absolute;overflow:hidden}.aqsp-selection-fill{mix-blend-mode:multiply;background:#3b82f62e;border:1px solid #2563ebd9;position:absolute;top:0;left:0}.aqsp-active-frame{box-sizing:border-box;border:2px solid #2563eb;position:absolute;top:0;left:0}.aqsp-drag-rect{background:#3b82f61a;border:1px solid #2563eb;position:absolute;top:0;left:0}.aqsp-drag-rect[data-aqsp-drag-mode=subtract]{background:#dc262614;border:1px dashed #dc2626}.aqsp-drag-rect[data-aqsp-drag-mode=point]{background:#16a34a14;border:1px dashed #16a34a}.aqsp-band{opacity:var(--aqsp-band-opacity);pointer-events:none;position:absolute;top:0;left:0}.aqsp-band[data-aqsp-band=row]{background:var(--aqsp-band-row)}.aqsp-band[data-aqsp-band=column]{background:var(--aqsp-band-column)}.aqsp-band[data-aqsp-band=intersection]{background:var(--aqsp-band-intersection)}.aqsp-clip-marquee{position:absolute;top:0;left:0}.aqsp-clip-marquee:after{content:"";top:var(--aqsp-clip-outset-top,-3px);right:var(--aqsp-clip-outset-right,-3px);bottom:var(--aqsp-clip-outset-bottom,-3px);left:var(--aqsp-clip-outset-left,-3px);background-image:linear-gradient(90deg,#2563eb 50%,#fff 50%),linear-gradient(90deg,#2563eb 50%,#fff 50%),linear-gradient(#fff 50%,#2563eb 50%),linear-gradient(#fff 50%,#2563eb 50%);background-position:0 0,8px 100%,0 8px,100% 0;background-repeat:repeat-x,repeat-x,repeat-y,repeat-y;background-size:8px 2px,8px 2px,2px 8px,2px 8px;animation:.4s linear infinite aqsp-marching-ants;position:absolute}@keyframes aqsp-marching-ants{to{background-position:8px 0,0 100%,0 0,100% 8px}}@media (prefers-reduced-motion:reduce){.aqsp-clip-marquee:after{animation:none}}.aqsp-col-resize-strip{cursor:col-resize;height:100%;position:absolute;top:0}.aqsp-row-resize-strip{cursor:row-resize;position:absolute;left:0;right:0}.aqsp-col-header .aqsp-unhide-marker{cursor:col-resize;opacity:.7;background:repeating-linear-gradient(90deg,#2563eb 0 1px,#0000 1px 3px);height:100%;position:absolute;top:0}.aqsp-row-header .aqsp-unhide-marker{cursor:row-resize;opacity:.7;background:repeating-linear-gradient(#2563eb 0 1px,#0000 1px 3px);position:absolute;left:0;right:0}.aqsp-resize-guide{pointer-events:none;background:#2563eb;position:absolute;top:0;left:0}.aqsp-autofill-handle{box-sizing:content-box;pointer-events:auto;cursor:crosshair;background:#2563eb;border:1px solid #fff;width:6px;height:6px;position:absolute;top:0;left:0}.aqsp-autofill-preview{border:1px dashed #16a34a;position:absolute;top:0;left:0}@media (forced-colors:active){.aqsp-band{outline-offset:-2px;background:0 0;outline:2px solid highlight}.aqsp-cell[data-aqsp-note]:after,.aqsp-cell[data-aqsp-comment]:after,.aqsp-cell[data-aqsp-invalid]:before{border:1px solid canvastext;width:6px;height:6px}.aqsp-drag-rect[data-aqsp-drag-mode=point]{background:0 0;border-color:highlight}.aqsp-selection-fill{outline-offset:-2px;background:0 0;border:none;outline:2px solid highlight}.aqsp-clip-marquee:after{background-image:none;border:2px dashed highlight;animation:none}}.aqsp-col-header-cell[data-aqsp-in-selection],.aqsp-row-header-cell[data-aqsp-in-selection]{color:#1d4ed8;background:#dbeafe;font-weight:600}.aqsp-live-region{clip-path:inset(50%);white-space:nowrap;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.aqsp-sink{opacity:0;resize:none;pointer-events:auto;cursor:default;border:0;width:1px;height:1px;padding:0;position:absolute}.aqsp-sink[data-aqsp-editing]{opacity:1;z-index:1;box-sizing:border-box;color:#1f2937;caret-color:#1f2937;white-space:pre;outline-offset:-2px;cursor:text;background:#fff;outline:2px solid #2563eb;padding:0 4px;font-family:inherit;font-size:13px;line-height:1;overflow:hidden}.aqsp-sink[data-aqsp-pointing]{caret-color:#16a34a}.aqsp-popover-layer{pointer-events:none;position:absolute;inset:0;overflow:visible}.aqsp-popover-anchor{pointer-events:none;position:absolute;top:0;left:0}.aqsp-popover-anchor[inert]{visibility:hidden}.aqsp-popover{color:#1f2937;text-align:start;white-space:normal;cursor:default;pointer-events:auto;background:#fff;border:1px solid #d1d5db;border-radius:8px;flex-direction:column;width:320px;max-width:320px;font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica Neue,Noto Sans JP,sans-serif;font-size:13px;line-height:1.4;display:flex;position:absolute;overflow:hidden;box-shadow:0 1px 2px #00000014,0 8px 24px #00000024}.aqsp-popover:focus-visible{outline-offset:-2px;outline:2px solid #2563eb}.aqsp-popover[data-aqsp-popover-frame=note-tip]{background:#fffbeb;border-color:#f59e0b;box-shadow:0 2px 8px #0000001f}.aqsp-popover-note,.aqsp-popover-comment,.aqsp-popover-link,.aqsp-popover-message{flex-direction:column;min-height:0;padding:10px 12px;display:flex}.aqsp-popover-list{flex-direction:column;min-height:0;padding:6px;display:flex}.aqsp-popover-note-tip{overscroll-behavior:contain;white-space:pre-wrap;overflow-wrap:anywhere;min-height:0;padding:8px 10px;overflow-y:auto}[data-aqsp-popover][data-aqsp-popover-rejected]{border-inline-start:3px solid #dc2626;padding-inline-start:9px}.aqsp-popover-title{color:#374151;align-items:baseline;gap:6px;margin:0 0 6px;font-size:12px;font-weight:600;display:flex}.aqsp-popover-count{color:#6b7280;font-weight:400}.aqsp-popover-textarea,.aqsp-popover-input{width:100%;color:inherit;font:inherit;background:#fff;border:1px solid #d1d5db;border-radius:6px;margin:0;padding:5px 8px;line-height:1.4;display:block}.aqsp-popover-textarea{resize:none;white-space:pre-wrap;flex:auto;height:104px;min-height:56px;overflow:auto}.aqsp-popover-comment .aqsp-popover-textarea{flex:none;height:64px}.aqsp-popover-input{flex:none;height:28px}.aqsp-popover-textarea:focus-visible,.aqsp-popover-input:focus-visible{outline-offset:-1px;border-color:#2563eb;outline:2px solid #2563eb}.aqsp-popover-textarea[aria-invalid=true],.aqsp-popover-input[aria-invalid=true]{border-color:#dc2626}.aqsp-popover-textarea[aria-invalid=true]:focus-visible,.aqsp-popover-input[aria-invalid=true]:focus-visible{border-color:#dc2626;outline-color:#dc2626}.aqsp-popover-hint{color:#6b7280;margin:4px 0 0;font-size:12px}[data-aqsp-popover-rejected] .aqsp-popover-hint{color:#b91c1c}.aqsp-popover-field{flex-direction:column;flex:none;gap:3px;margin:0 0 8px;display:flex}.aqsp-popover-field-label{color:#6b7280;font-size:12px}.aqsp-popover-actions{flex:none;align-items:center;gap:6px;margin-top:8px;display:flex}.aqsp-popover-button{color:#1f2937;min-width:56px;height:26px;font:inherit;cursor:pointer;background:#f9fafb;border:1px solid #d1d5db;border-radius:6px;justify-content:center;align-items:center;margin:0;padding:0 10px;font-size:12px;font-weight:500;line-height:1;display:inline-flex}.aqsp-popover-button:not(:disabled):hover{background:#f3f4f6;border-color:#9ca3af}.aqsp-popover-button:focus-visible{outline-offset:1px;outline:2px solid #2563eb}.aqsp-popover-button:disabled{opacity:.5;cursor:not-allowed}.aqsp-popover-button[data-aqsp-popover-save],.aqsp-popover-button[data-aqsp-popover-post]{color:#fff;background:#2563eb;border-color:#2563eb}.aqsp-popover-button[data-aqsp-popover-save]:not(:disabled):hover,.aqsp-popover-button[data-aqsp-popover-post]:not(:disabled):hover{background:#1d4ed8;border-color:#1d4ed8}.aqsp-popover-button[data-aqsp-popover-close]{color:#6b7280;background:0 0;border-color:#0000;width:26px;min-width:0;margin-inline-start:auto;padding:0;font-size:16px;font-weight:400}.aqsp-popover-button[data-aqsp-popover-close]:not(:disabled):hover{color:#1f2937;background:#f3f4f6;border-color:#0000}.aqsp-popover-thread{overscroll-behavior:contain;flex:auto;min-height:48px;max-height:220px;margin:0 0 8px;padding:0;list-style:none;overflow-y:auto}.aqsp-popover-comment-item{border-top:1px solid #f3f4f6;padding:6px 0}.aqsp-popover-comment-item:first-child{border-top:0;padding-top:0}.aqsp-popover-comment-author{color:#111827;font-size:12px;font-weight:600}.aqsp-popover-comment-time{color:#6b7280;font-variant-numeric:tabular-nums;margin-inline-start:6px;font-size:11px}.aqsp-popover-comment-text{white-space:pre-wrap;overflow-wrap:anywhere;margin:2px 0 0}.aqsp-popover-list .aqsp-popover-input{margin-bottom:4px}.aqsp-popover-options{overscroll-behavior:contain;flex:auto;min-height:0;max-height:240px;overflow-y:auto}.aqsp-popover-option{white-space:nowrap;text-overflow:ellipsis;cursor:pointer;border-radius:4px;padding:5px 8px;overflow:hidden}.aqsp-popover-option:hover{background:#f3f4f6}.aqsp-popover-option[aria-selected=true]{color:#1e3a8a;background:#dbeafe}.aqsp-popover-option:focus-visible{outline-offset:-2px;outline:2px solid #2563eb}.aqsp-popover-empty{color:#6b7280;padding:6px 8px;font-size:12px}.aqsp-popover-message{padding-inline-end:40px;position:relative}.aqsp-popover-message .aqsp-popover-title{margin-bottom:2px}.aqsp-popover-message-text{white-space:pre-wrap;overflow-wrap:anywhere;margin:0}.aqsp-popover-message .aqsp-popover-button[data-aqsp-popover-close]{position:absolute;inset-block-start:6px;inset-inline-end:6px}.aqsp-popover-message[data-aqsp-popover-rejected]{background:#fef2f2}.aqsp-popover-message[data-aqsp-popover-rejected] .aqsp-popover-title{color:#b91c1c}@media (forced-colors:active){.aqsp-popover-option:hover,.aqsp-popover-option[aria-selected=true]{outline-offset:-2px;outline:2px solid highlight}.aqsp-popover-textarea[aria-invalid=true],.aqsp-popover-input[aria-invalid=true]{border-style:dashed}.aqsp-popover-button[data-aqsp-popover-close]{border-color:buttontext}}[data-aqsp-empty]{width:100%;height:100%;display:block}.aqsp-freeze-divider{background:var(--aqsp-divider-color,#9ca3af);pointer-events:none;position:absolute}.aqsp-freeze-divider-v{width:var(--aqsp-divider-width,2px)}.aqsp-freeze-divider-h{height:var(--aqsp-divider-width,2px)}@media (forced-colors:active){.aqsp-freeze-divider{background:canvastext}}}@layer utilities;
|
|
3
|
+
@layer theme,base;@layer components{@layer components{.aqvs-scroll-pane{display:flex;position:relative}.aqvs-scroll-pane[data-self-measured=true]{height:100%}.aqvs-scroll-pane-content{box-sizing:border-box;flex:1;height:100%;position:relative;overflow:hidden}.aqvs-tap-scroll-circle{touch-action:none;-webkit-user-select:none;user-select:none;justify-content:center;align-items:center;transition-property:transform;transition-duration:.1s;transition-timing-function:cubic-bezier(0,0,.2,1);display:flex;position:relative}.aqvs-tap-scroll-circle-visual-outer{border-radius:9999px;position:absolute;inset:0}.aqvs-tap-scroll-circle-gradient{background:linear-gradient(to bottom right,#1d4ed899,#60a5fa8c,#bfdbfe66);border:1px solid #fff6;border-radius:9999px;position:absolute;inset:0;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.aqvs-tap-scroll-circle-inner{border-radius:9999px;position:absolute;inset:18%}.aqvs-sample-visual-pupil{box-sizing:border-box;background-color:#ffffffd9;border:1px solid #ffffff80;border-radius:9999px;position:absolute;top:50%;left:50%}.aqvs-sample-visual-highlight{background-color:#fffc;border-radius:9999px;position:absolute;top:50%;left:50%}.aqvs-sample-visual-rod{background-color:#ffffff80;border-radius:9999px;position:absolute;top:50%;left:50%}.aqvs-scrollbar{z-index:50;cursor:default;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;touch-action:none;background-color:#fff;position:relative}.aqvs-scrollbar-horizontal{flex-direction:row;align-items:stretch;display:flex}.aqvs-scrollbar-vertical{flex-direction:column;align-items:stretch;display:flex}.aqvs-scrollbar[data-visible=false],.aqvs-scrollbar-thumb-wrapper[data-visible=false]{opacity:0;pointer-events:none}.aqvs-scrollbar-tap-circle-wrapper{pointer-events:auto;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);position:absolute}.aqvs-scrollbar-arrow-button{color:#313131;letter-spacing:normal;background-color:#e0e0e0;border:0 solid;justify-content:center;align-items:center;padding:0;font-family:system-ui,sans-serif;font-size:.75rem;font-weight:400;line-height:1rem;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);display:flex}.aqvs-scrollbar-arrow-button:focus{outline-offset:2px;outline:2px solid #0000}.aqvs-scrollbar-arrow-button:focus-visible{outline-offset:1px;outline:2px solid #60a5fa}.aqvs-scrollbar-arrow-button:disabled{cursor:not-allowed;opacity:.5}.aqvs-scrollbar-arrow-button:enabled:hover{background-color:#d4d4d4}.aqvs-scrollbar-track{background-color:#f5f5f5;flex:1;position:relative}.aqvs-scrollbar-overlay{pointer-events:none;position:absolute;inset:0}.aqvs-scrollbar-thumb-wrapper{touch-action:none;position:absolute}.aqvs-scrollbar-thumb{transform-origin:50%;background-color:#7f7f7f;transition:transform 80ms ease-out;position:absolute}.aqvs-scrollbar-thumb[data-thumb-state=disabled]{transition:none;transform:none}.aqvs-scrollbar-thumb[data-thumb-state=hover]{background-color:#5f5f5f}.aqvs-scrollbar-thumb[data-thumb-state=dragging]{background-color:#4f4f4f;transition:transform 60ms ease-out}.aqvs-scrollbar-thumb-horizontal{inset:1.5px 0}.aqvs-scrollbar-thumb-horizontal[data-thumb-state=hover]{top:-.5px;bottom:-.5px;transform:scaleY(1.06)}.aqvs-scrollbar-thumb-horizontal[data-thumb-state=dragging]{top:-.5px;bottom:-.5px;transform:scaleY(1.12)}.aqvs-scrollbar-thumb-vertical{inset:0 1.5px}.aqvs-scrollbar-thumb-vertical[data-thumb-state=hover]{left:-.5px;right:-.5px;transform:scaleX(1.06)}.aqvs-scrollbar-thumb-vertical[data-thumb-state=dragging]{left:-.5px;right:-.5px;transform:scaleX(1.12)}.aqvs-scroll-to-edge-button{pointer-events:auto;cursor:pointer;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;color:#fff;text-transform:uppercase;letter-spacing:.05em;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);background-color:#1f2937cc;border:0 solid;border-radius:9999px;padding:.25rem 3rem;font-family:inherit;font-size:10px;font-weight:500;line-height:1.5;transition-property:transform,background-color;transition-duration:.15s;transform:none;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.aqvs-scroll-to-edge-button:focus{outline-offset:2px;outline:2px solid #0000}.aqvs-scroll-to-edge-button:focus-visible{outline-offset:1px;outline:2px solid #60a5fa}.aqvs-scroll-to-edge-button:hover{background-color:#374151}.aqvs-scroll-to-edge-button:active{transform:scale(.95)}.aqvs-scroll-to-edge-overlay{pointer-events:none;z-index:10;transition-property:opacity;transition-duration:.5s;position:absolute;inset:0}.aqvs-scroll-to-edge-overlay[data-visible=true]{opacity:1}.aqvs-scroll-to-edge-overlay[data-visible=false]{opacity:0}.aqvs-scroll-to-edge-overlay[data-visible=false] .aqvs-scroll-to-edge-button{pointer-events:none}.aqvs-scroll-to-edge-button-container{justify-content:center;display:flex;position:absolute;left:0;right:0}.aqvs-scroll-to-edge-button-container-top{top:.5rem}.aqvs-scroll-to-edge-button-container-bottom{bottom:.5rem}.aqvs-no-items-container{width:100%;position:absolute;top:0}.aqvs-no-items-text{text-align:center;color:#6b7280}.aqvs-item-container,.aqvs-bottom-inset,.aqvs-items-wrapper{width:100%;position:absolute}.aqvs-live-region{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.aqvs-grid{grid-template-rows:minmax(0,1fr) auto;width:100%;height:100%;display:grid;position:relative;overflow:hidden}.aqvs-grid-has-frozen-rows{grid-template-rows:auto minmax(0,1fr) auto}.aqvs-grid-has-trailing-rows{grid-template-rows:minmax(0,1fr) auto auto}.aqvs-grid-has-frozen-rows.aqvs-grid-has-trailing-rows{grid-template-rows:auto minmax(0,1fr) auto auto}.aqvs-grid-frozen-rows{min-width:0;position:relative;overflow:hidden}.aqvs-grid-frozen-band-row{position:absolute;left:0;overflow:hidden}.aqvs-grid-trailing-rows{min-width:0;position:relative;overflow:hidden}.aqvs-grid-trailing-rows-inner{position:absolute;bottom:0;left:0;right:0}.aqvs-grid-trailing-band-row{position:absolute;left:0;overflow:hidden}.aqvs-grid-main{min-width:0;min-height:0;position:relative}.aqvs-grid-row{width:100%;height:100%;transform:translateX(var(--aqvs-grid-hx-residual,0px));position:relative;overflow:visible}.aqvs-grid-cell{height:100%;position:absolute;top:0;overflow:hidden}.aqvs-grid-row.aqvs-grid-row-frozen-host{transform:none}.aqvs-grid-row-scroll{height:100%;top:0;left:var(--aqvs-grid-frozen-width,0px);right:var(--aqvs-grid-trailing-width,0px);position:absolute;overflow:hidden}.aqvs-grid-row-scroll-inner{width:100%;height:100%;transform:translateX(calc(var(--aqvs-grid-hx-residual,0px) - var(--aqvs-grid-frozen-width,0px)));position:absolute;top:0;left:0}.aqvs-grid-row-trailing{height:100%;top:0;left:max(var(--aqvs-grid-frozen-width,0px), calc(100% - var(--aqvs-grid-trailing-width,0px)));position:absolute;right:0;overflow:hidden}.aqvs-grid-row-trailing-inner{height:100%;width:var(--aqvs-grid-trailing-width,0px);position:absolute;top:0;right:0}.aqvs-grid-hbar-strip{min-width:0;padding-left:var(--aqvs-grid-frozen-width,0px);position:relative}.aqvs-grid-live-region{clip:rect(0 0 0 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.aqvs-grid-tap-circle-wrapper{z-index:50;pointer-events:auto;position:absolute}.aqvs-grid-tap-circle{transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}@media (forced-colors:active){.aqvs-tap-scroll-circle{outline-offset:-1px;border-radius:50%;outline:1px solid canvastext}}}.aqsp-root,.aqsp-root *,.aqsp-root :before,.aqsp-root :after{box-sizing:border-box}.aqsp-root{--aqsp-row-header-width:64px;--aqsp-col-header-height:24px;--aqsp-band-row:#0084ff;--aqsp-band-column:#42ff4f;--aqsp-band-intersection:#e1ff00;--aqsp-band-opacity:.2;color:#1f2937;background:#fff;width:100%;height:100%;font-size:13px;line-height:1;position:relative;overflow:hidden}.aqsp-zoom{grid-template-columns:var(--aqsp-row-header-width) minmax(0, 1fr);grid-template-rows:var(--aqsp-col-header-height) minmax(0, 1fr);transform-origin:0 0;width:100%;height:100%;display:grid}.aqsp-corner{background:#f3f4f6;border-bottom:1px solid #d1d5db;border-right:1px solid #d1d5db;grid-area:1/1}.aqsp-col-header{-webkit-user-select:none;user-select:none;background:#f3f4f6;border-bottom:1px solid #d1d5db;grid-area:1/2;position:relative;overflow:hidden}.aqsp-col-header-scroll{height:100%;position:absolute;top:0;right:0;overflow:hidden}.aqsp-row-header-scroll{width:100%;position:absolute;bottom:0;left:0;overflow:hidden}.aqsp-col-header-track{will-change:transform;position:absolute;inset:0}.aqsp-col-header-cell{white-space:nowrap;color:#4b5563;border-right:1px solid #e5e7eb;justify-content:center;align-items:center;height:100%;display:flex;position:absolute;top:0;overflow:hidden}.aqsp-row-header{-webkit-user-select:none;user-select:none;background:#f3f4f6;border-right:1px solid #d1d5db;grid-area:2/1;position:relative;overflow:hidden}.aqsp-row-header-track{will-change:transform;position:absolute;top:0;left:0;right:0}.aqsp-row-header-cell{white-space:nowrap;color:#4b5563;font-variant-numeric:tabular-nums;border-bottom:1px solid #e5e7eb;justify-content:flex-end;align-items:center;padding-right:6px;display:flex;position:absolute;left:0;right:0;overflow:hidden}.aqsp-body{position:absolute;inset:0}.aqsp-row{cursor:default}.aqsp-cell{white-space:nowrap;background:#fff;border-bottom:1px solid #e5e7eb;border-right:1px solid #e5e7eb;align-items:center;padding:0 4px;display:flex}.aqsp-cell[data-aqsp-valign=top]{align-items:flex-start}.aqsp-cell[data-aqsp-valign=bottom]{align-items:flex-end}.aqsp-cell[data-aqsp-wrap]{white-space:pre-wrap;overflow-wrap:anywhere}.aqsp-cell[data-aqsp-note]:after,.aqsp-cell[data-aqsp-comment]:after{content:"";pointer-events:none;border:5px solid #4b5563;border-color:#4b5563 #4b5563 #0000 #0000;width:0;height:0;position:absolute;top:0;right:0}.aqsp-cell[data-aqsp-comment]:after{border-top-color:#f97316;border-right-color:#f97316}.aqsp-cell[data-aqsp-invalid]:before{content:"";pointer-events:none;border:5px solid #0000;border-color:#0000 #0000 #dc2626 #dc2626;width:0;height:0;position:absolute;bottom:0;left:0}.aqsp-cell[data-aqsp-link]{color:#1d4ed8;text-decoration:underline}.aqsp-body-stack{grid-area:2/2;position:relative}.aqsp-overlay{pointer-events:none;position:absolute;inset:0;overflow:hidden}.aqsp-overlay-quad{pointer-events:none;position:absolute;overflow:hidden}.aqsp-selection-fill{mix-blend-mode:multiply;background:#3b82f62e;border:1px solid #2563ebd9;position:absolute;top:0;left:0}.aqsp-active-frame{box-sizing:border-box;border:2px solid #2563eb;position:absolute;top:0;left:0}.aqsp-drag-rect{background:#3b82f61a;border:1px solid #2563eb;position:absolute;top:0;left:0}.aqsp-drag-rect[data-aqsp-drag-mode=subtract]{background:#dc262614;border:1px dashed #dc2626}.aqsp-drag-rect[data-aqsp-drag-mode=point]{background:#16a34a14;border:1px dashed #16a34a}.aqsp-band{opacity:var(--aqsp-band-opacity);pointer-events:none;position:absolute;top:0;left:0}.aqsp-band[data-aqsp-band=row]{background:var(--aqsp-band-row)}.aqsp-band[data-aqsp-band=column]{background:var(--aqsp-band-column)}.aqsp-band[data-aqsp-band=intersection]{background:var(--aqsp-band-intersection)}.aqsp-clip-marquee{position:absolute;top:0;left:0}.aqsp-clip-marquee:after{content:"";top:var(--aqsp-clip-outset-top,-3px);right:var(--aqsp-clip-outset-right,-3px);bottom:var(--aqsp-clip-outset-bottom,-3px);left:var(--aqsp-clip-outset-left,-3px);background-image:linear-gradient(90deg,#2563eb 50%,#fff 50%),linear-gradient(90deg,#2563eb 50%,#fff 50%),linear-gradient(#fff 50%,#2563eb 50%),linear-gradient(#fff 50%,#2563eb 50%);background-position:0 0,8px 100%,0 8px,100% 0;background-repeat:repeat-x,repeat-x,repeat-y,repeat-y;background-size:8px 2px,8px 2px,2px 8px,2px 8px;animation:.4s linear infinite aqsp-marching-ants;position:absolute}@keyframes aqsp-marching-ants{to{background-position:8px 0,0 100%,0 0,100% 8px}}@media (prefers-reduced-motion:reduce){.aqsp-clip-marquee:after{animation:none}}.aqsp-col-resize-strip{cursor:col-resize;height:100%;position:absolute;top:0}.aqsp-row-resize-strip{cursor:row-resize;position:absolute;left:0;right:0}.aqsp-col-header .aqsp-unhide-marker{cursor:col-resize;opacity:.7;background:repeating-linear-gradient(90deg,#2563eb 0 1px,#0000 1px 3px);height:100%;position:absolute;top:0}.aqsp-row-header .aqsp-unhide-marker{cursor:row-resize;opacity:.7;background:repeating-linear-gradient(#2563eb 0 1px,#0000 1px 3px);position:absolute;left:0;right:0}.aqsp-resize-guide{pointer-events:none;background:#2563eb;position:absolute;top:0;left:0}.aqsp-autofill-handle{box-sizing:content-box;pointer-events:auto;cursor:crosshair;background:#2563eb;border:1px solid #fff;width:6px;height:6px;position:absolute;top:0;left:0}.aqsp-autofill-preview{border:1px dashed #16a34a;position:absolute;top:0;left:0}@media (forced-colors:active){.aqsp-band{outline-offset:-2px;background:0 0;outline:2px solid highlight}.aqsp-cell[data-aqsp-note]:after,.aqsp-cell[data-aqsp-comment]:after,.aqsp-cell[data-aqsp-invalid]:before{border:1px solid canvastext;width:6px;height:6px}.aqsp-drag-rect[data-aqsp-drag-mode=point]{background:0 0;border-color:highlight}.aqsp-selection-fill{outline-offset:-2px;background:0 0;border:none;outline:2px solid highlight}.aqsp-clip-marquee:after{background-image:none;border:2px dashed highlight;animation:none}}.aqsp-col-header-cell[data-aqsp-in-selection],.aqsp-row-header-cell[data-aqsp-in-selection]{color:#1d4ed8;background:#dbeafe;font-weight:600}.aqsp-live-region{clip-path:inset(50%);white-space:nowrap;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.aqsp-sink{opacity:0;resize:none;pointer-events:auto;cursor:default;border:0;width:1px;height:1px;padding:0;position:absolute}.aqsp-sink[data-aqsp-editing]{opacity:1;z-index:1;box-sizing:border-box;color:#1f2937;caret-color:#1f2937;white-space:pre;outline-offset:-2px;cursor:text;background:#fff;outline:2px solid #2563eb;padding:0 4px;font-family:inherit;font-size:13px;line-height:1;overflow:hidden}.aqsp-sink[data-aqsp-pointing]{caret-color:#16a34a}.aqsp-popover-layer{pointer-events:none;position:absolute;inset:0;overflow:visible}.aqsp-popover-anchor{pointer-events:none;position:absolute;top:0;left:0}.aqsp-popover-anchor[inert]{visibility:hidden}.aqsp-popover{color:#1f2937;text-align:start;white-space:normal;cursor:default;pointer-events:auto;background:#fff;border:1px solid #d1d5db;border-radius:8px;flex-direction:column;width:320px;max-width:320px;font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica Neue,Noto Sans JP,sans-serif;font-size:13px;line-height:1.4;display:flex;position:absolute;overflow:hidden;box-shadow:0 1px 2px #00000014,0 8px 24px #00000024}.aqsp-popover:focus-visible{outline-offset:-2px;outline:2px solid #2563eb}.aqsp-popover[data-aqsp-popover-frame=note-tip]{background:#fffbeb;border-color:#f59e0b;box-shadow:0 2px 8px #0000001f}.aqsp-popover-note,.aqsp-popover-comment,.aqsp-popover-link,.aqsp-popover-message{flex-direction:column;min-height:0;padding:10px 12px;display:flex}.aqsp-popover-list{flex-direction:column;min-height:0;padding:6px;display:flex}.aqsp-popover-note-tip{overscroll-behavior:contain;white-space:pre-wrap;overflow-wrap:anywhere;min-height:0;padding:8px 10px;overflow-y:auto}[data-aqsp-popover][data-aqsp-popover-rejected]{border-inline-start:3px solid #dc2626;padding-inline-start:9px}.aqsp-popover-title{color:#374151;align-items:baseline;gap:6px;margin:0 0 6px;font-size:12px;font-weight:600;display:flex}.aqsp-popover-count{color:#6b7280;font-weight:400}.aqsp-popover-textarea,.aqsp-popover-input{width:100%;color:inherit;font:inherit;background:#fff;border:1px solid #d1d5db;border-radius:6px;margin:0;padding:5px 8px;line-height:1.4;display:block}.aqsp-popover-textarea{resize:none;white-space:pre-wrap;flex:auto;height:104px;min-height:56px;overflow:auto}.aqsp-popover-comment .aqsp-popover-textarea{flex:none;height:64px}.aqsp-popover-input{flex:none;height:28px}.aqsp-popover-textarea:focus-visible,.aqsp-popover-input:focus-visible{outline-offset:-1px;border-color:#2563eb;outline:2px solid #2563eb}.aqsp-popover-textarea[aria-invalid=true],.aqsp-popover-input[aria-invalid=true]{border-color:#dc2626}.aqsp-popover-textarea[aria-invalid=true]:focus-visible,.aqsp-popover-input[aria-invalid=true]:focus-visible{border-color:#dc2626;outline-color:#dc2626}.aqsp-popover-hint{color:#6b7280;margin:4px 0 0;font-size:12px}[data-aqsp-popover-rejected] .aqsp-popover-hint{color:#b91c1c}.aqsp-popover-field{flex-direction:column;flex:none;gap:3px;margin:0 0 8px;display:flex}.aqsp-popover-field-label{color:#6b7280;font-size:12px}.aqsp-popover-actions{flex:none;align-items:center;gap:6px;margin-top:8px;display:flex}.aqsp-popover-button{color:#1f2937;min-width:56px;height:26px;font:inherit;cursor:pointer;background:#f9fafb;border:1px solid #d1d5db;border-radius:6px;justify-content:center;align-items:center;margin:0;padding:0 10px;font-size:12px;font-weight:500;line-height:1;display:inline-flex}.aqsp-popover-button:not(:disabled):hover{background:#f3f4f6;border-color:#9ca3af}.aqsp-popover-button:focus-visible{outline-offset:1px;outline:2px solid #2563eb}.aqsp-popover-button:disabled{opacity:.5;cursor:not-allowed}.aqsp-popover-button[data-aqsp-popover-save],.aqsp-popover-button[data-aqsp-popover-post]{color:#fff;background:#2563eb;border-color:#2563eb}.aqsp-popover-button[data-aqsp-popover-save]:not(:disabled):hover,.aqsp-popover-button[data-aqsp-popover-post]:not(:disabled):hover{background:#1d4ed8;border-color:#1d4ed8}.aqsp-popover-button[data-aqsp-popover-close]{color:#6b7280;background:0 0;border-color:#0000;width:26px;min-width:0;margin-inline-start:auto;padding:0;font-size:16px;font-weight:400}.aqsp-popover-button[data-aqsp-popover-close]:not(:disabled):hover{color:#1f2937;background:#f3f4f6;border-color:#0000}.aqsp-popover-thread{overscroll-behavior:contain;flex:auto;min-height:48px;max-height:220px;margin:0 0 8px;padding:0;list-style:none;overflow-y:auto}.aqsp-popover-comment-item{border-top:1px solid #f3f4f6;padding:6px 0}.aqsp-popover-comment-item:first-child{border-top:0;padding-top:0}.aqsp-popover-comment-author{color:#111827;font-size:12px;font-weight:600}.aqsp-popover-comment-time{color:#6b7280;font-variant-numeric:tabular-nums;margin-inline-start:6px;font-size:11px}.aqsp-popover-comment-text{white-space:pre-wrap;overflow-wrap:anywhere;margin:2px 0 0}.aqsp-popover-list .aqsp-popover-input{margin-bottom:4px}.aqsp-popover-options{overscroll-behavior:contain;flex:auto;min-height:0;max-height:240px;overflow-y:auto}.aqsp-popover-option{white-space:nowrap;text-overflow:ellipsis;cursor:pointer;border-radius:4px;padding:5px 8px;overflow:hidden}.aqsp-popover-option:hover{background:#f3f4f6}.aqsp-popover-option[aria-selected=true]{color:#1e3a8a;background:#dbeafe}.aqsp-popover-option:focus-visible{outline-offset:-2px;outline:2px solid #2563eb}.aqsp-popover-empty{color:#6b7280;padding:6px 8px;font-size:12px}.aqsp-popover-message{padding-inline-end:40px;position:relative}.aqsp-popover-message .aqsp-popover-title{margin-bottom:2px}.aqsp-popover-message-text{white-space:pre-wrap;overflow-wrap:anywhere;margin:0}.aqsp-popover-message .aqsp-popover-button[data-aqsp-popover-close]{position:absolute;inset-block-start:6px;inset-inline-end:6px}.aqsp-popover-message[data-aqsp-popover-rejected]{background:#fef2f2}.aqsp-popover-message[data-aqsp-popover-rejected] .aqsp-popover-title{color:#b91c1c}@media (forced-colors:active){.aqsp-popover-option:hover,.aqsp-popover-option[aria-selected=true]{outline-offset:-2px;outline:2px solid highlight}.aqsp-popover-textarea[aria-invalid=true],.aqsp-popover-input[aria-invalid=true]{border-style:dashed}.aqsp-popover-button[data-aqsp-popover-close]{border-color:buttontext}}[data-aqsp-empty]{width:100%;height:100%;display:block}.aqsp-freeze-divider{background:var(--aqsp-divider-color,#9ca3af);pointer-events:none;position:absolute}.aqsp-freeze-divider-v{width:var(--aqsp-divider-width,2px)}.aqsp-freeze-divider-h{height:var(--aqsp-divider-width,2px)}@media (forced-colors:active){.aqsp-freeze-divider{background:canvastext}}}@layer utilities;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiquants/spreadsheet",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Excel-grade spreadsheet grid for React: multi-cell selection, TSV/HTML clipboard, in-place editing on top of @aiquants/virtualscroll",
|
|
5
5
|
"sideEffects": [
|
|
6
6
|
"**/*.css"
|
|
@@ -34,11 +34,12 @@
|
|
|
34
34
|
"LICENSE.md"
|
|
35
35
|
],
|
|
36
36
|
"peerDependencies": {
|
|
37
|
+
"@aiquants/virtualscroll": "^3.6.1",
|
|
37
38
|
"react": "^19.2.7",
|
|
38
|
-
"react-dom": "^19.2.7"
|
|
39
|
-
"@aiquants/virtualscroll": "^3.5.0"
|
|
39
|
+
"react-dom": "^19.2.7"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
+
"@aiquants/virtualscroll": "3.6.1",
|
|
42
43
|
"@playwright/test": "^1.59.1",
|
|
43
44
|
"@tailwindcss/cli": "^4.1.18",
|
|
44
45
|
"@testing-library/jest-dom": "^6.9.1",
|
|
@@ -55,8 +56,7 @@
|
|
|
55
56
|
"typescript": "^5.9.3",
|
|
56
57
|
"vite": "^7.2.7",
|
|
57
58
|
"vite-plugin-dts": "^4.5.4",
|
|
58
|
-
"vitest": "^3.2.4"
|
|
59
|
-
"@aiquants/virtualscroll": "3.5.0"
|
|
59
|
+
"vitest": "^3.2.4"
|
|
60
60
|
},
|
|
61
61
|
"keywords": [
|
|
62
62
|
"react",
|
package/src/Spreadsheet.tsx
CHANGED
|
@@ -39,9 +39,10 @@
|
|
|
39
39
|
* openPopoverRef / hideNoteTipRef / pasteRef / pendingPasteOptionsRef / ポップオーバーアンカー) を所有する。
|
|
40
40
|
*/
|
|
41
41
|
|
|
42
|
-
import type { VirtualGridHandle, VirtualGridRange } from "@aiquants/virtualscroll"
|
|
42
|
+
import type { ScrollBarTapCircleOptions, VirtualGridHandle, VirtualGridRange } from "@aiquants/virtualscroll"
|
|
43
43
|
import type { ProfilerOnRenderCallback } from "react"
|
|
44
44
|
import { forwardRef, type ReactNode, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react"
|
|
45
|
+
import type { ClipChannel } from "./clipboard/clipPayload.ts"
|
|
45
46
|
import type { PasteOptions } from "./clipboard/pasteSpecial.ts"
|
|
46
47
|
import { type ClipInfo, type PasteOutcome, type PasteSheet, useClipboard } from "./clipboard/useClipboard.ts"
|
|
47
48
|
import { type BorderPreset, type CommandRunner, useCommands } from "./commands/useCommands.ts"
|
|
@@ -62,7 +63,7 @@ import { type CellCoord, type CellRange, clampCoord, makeRange, rangeSize, type
|
|
|
62
63
|
import { type CellBorder, type CellFormat, type CellFormatPatch, formatCellValue } from "./model/cellFormat.ts"
|
|
63
64
|
import { type EditPaint, IDLE_EDIT_PAINT, normalizeColor, resolveEditPaint } from "./model/cellPaint.ts"
|
|
64
65
|
import { buildCfIndex, type CfIndex, type CfMemo, createCfMemo, resolvePaintedFormat } from "./model/conditionalFormat.ts"
|
|
65
|
-
import { type CellEdit, type EditContext, type EditRejection, type EditSource, lazyCellText, resolveCellAlign, type SpreadsheetDataAdapter } from "./model/dataAdapter.ts"
|
|
66
|
+
import { type CellEdit, type EditContext, type EditRejection, type EditSource, isReadOnlyCell, lazyCellText, type ReadOnlyRejection, readOnlyDeclared, resolveCellAlign, type SpreadsheetDataAdapter } from "./model/dataAdapter.ts"
|
|
66
67
|
import { type DataValidationRule, type ValidationVerdict, validateCellText } from "./model/dataValidation.ts"
|
|
67
68
|
import type { ReferenceAxis } from "./model/formulaRefs.ts"
|
|
68
69
|
import { colToViewportX, rowToViewportY } from "./model/frozenPartition.ts"
|
|
@@ -204,6 +205,55 @@ export type SpreadsheetProps = {
|
|
|
204
205
|
* frozenTrailingColumns の行双子。エンジン契約・取消シームは同文)。典型は固定合計行。
|
|
205
206
|
*/
|
|
206
207
|
frozenTrailingRows?: number
|
|
208
|
+
/**
|
|
209
|
+
* Tap-scroll circle tuning (v0.10 — the VirtualGrid `scrollBarOptions.tapScrollCircleOptions`
|
|
210
|
+
* engine-validated pass-through; LIVE prop): position and enable/disable of the touch
|
|
211
|
+
* tap-scroll circle. The engine's `resolveTapScrollCircleOptions` is the validation SSOT —
|
|
212
|
+
* undefined is the engine's declared absence signal (engine defaults), never a value
|
|
213
|
+
* fallback in this package.
|
|
214
|
+
* タップサークル調整 (v0.10 — VirtualGrid `scrollBarOptions.tapScrollCircleOptions` の
|
|
215
|
+
* エンジン検証素通し。ライブ prop): タッチのタップスクロールサークルの位置・有効 / 無効の
|
|
216
|
+
* 調整口。検証の SSOT はエンジンの `resolveTapScrollCircleOptions` — undefined はエンジン
|
|
217
|
+
* 宣言の不在信号 (エンジン既定) で、本パッケージでの値フォールバックなし。
|
|
218
|
+
*/
|
|
219
|
+
tapScrollCircleOptions?: ScrollBarTapCircleOptions
|
|
220
|
+
/**
|
|
221
|
+
* Row-axis overscan pass-through (the VirtualGrid `overscanRows` knob; LIVE prop): rows
|
|
222
|
+
* rendered beyond each vertical viewport edge. This slack is the only thing absorbing the
|
|
223
|
+
* engine's one-frame rendering-window lag during fast scrolling (the blank-band remedy —
|
|
224
|
+
* the slack in px is overscan × row height per edge). Undefined ⇒ the engine default (3).
|
|
225
|
+
* The engine clamps invalid values silently, so THIS surface owns the fail-fast: a
|
|
226
|
+
* non-integer or negative value throws a RangeError at render. Cost multiplies with the
|
|
227
|
+
* column axis (bounded by the engine cell cap).
|
|
228
|
+
* 行軸 overscan の素通し (VirtualGrid `overscanRows`。ライブ prop): 縦 viewport 端の外側へ
|
|
229
|
+
* 描画する行数。高速スクロール時の描画窓 1 フレーム遅れを吸収する唯一の余裕 (白帯対策 —
|
|
230
|
+
* 余裕 px は片端あたり overscan × 行高)。undefined はエンジン既定 (3)。エンジンは不正値を
|
|
231
|
+
* 無言クランプするため fail-fast は本層所有 — 非整数・負値は render 時 RangeError。費用は
|
|
232
|
+
* 列軸との積で増幅 (エンジンのセル上限で有界)。
|
|
233
|
+
*/
|
|
234
|
+
overscanRows?: number
|
|
235
|
+
/**
|
|
236
|
+
* Column-axis overscan pass-through (the VirtualGrid `overscanCols` knob; LIVE prop) — the
|
|
237
|
+
* column twin of `overscanRows`, same validation contract (non-negative integer or
|
|
238
|
+
* RangeError; undefined ⇒ engine default 3).
|
|
239
|
+
* 列軸 overscan の素通し (VirtualGrid `overscanCols`。ライブ prop) — `overscanRows` の
|
|
240
|
+
* 列双子。検証契約は同文 (非負整数、それ以外 RangeError。undefined ⇒ エンジン既定 3)。
|
|
241
|
+
*/
|
|
242
|
+
overscanCols?: number
|
|
243
|
+
/**
|
|
244
|
+
* Background layer rendered behind the scroll-band rows, inside the engine content element
|
|
245
|
+
* (the VirtualGrid `background` pass-through; LIVE prop). VIEWPORT-PINNED: the engine
|
|
246
|
+
* translates only its row wrapper, so this layer does NOT follow the scroll offset —
|
|
247
|
+
* periodic row ruling placed here slides against the real rows while scrolling. Suited to
|
|
248
|
+
* a static texture that reads as "content still arriving" in the overscan-exhausted band,
|
|
249
|
+
* not to row-aligned gridlines.
|
|
250
|
+
* スクロール帯行の背後・エンジンコンテンツ要素内に描画する背景レイヤー (VirtualGrid
|
|
251
|
+
* `background` の素通し。ライブ prop)。viewport 固定: エンジンが translate するのは行
|
|
252
|
+
* ラッパーだけで、この層はスクロール量に追従しない — 周期的な行罫線を置くとスクロール中に
|
|
253
|
+
* 実際の行へ対して滑る。overscan が尽きた帯で「まだ到着中」と読める静的テクスチャ向きで、
|
|
254
|
+
* 行整合の罫線には不向き。
|
|
255
|
+
*/
|
|
256
|
+
background?: ReactNode
|
|
207
257
|
/**
|
|
208
258
|
* Identity-stable contract (same as adapter): an inline arrow here defeats the CellView memo
|
|
209
259
|
* and turns every host re-render into a full visible re-slice (~800 cells at 16k columns).
|
|
@@ -255,6 +305,39 @@ export type SpreadsheetProps = {
|
|
|
255
305
|
* Escape は WCAG 2.1.2 のフォーカス脱出)。
|
|
256
306
|
*/
|
|
257
307
|
onEditRejected?: (error: unknown, rejection: EditRejection) => void
|
|
308
|
+
/**
|
|
309
|
+
* Read-only refusal notice (v0.10): fired once per refused gesture — the adapter was never
|
|
310
|
+
* called, so this is neither an EditRejection nor an operation limit. Never fired when the
|
|
311
|
+
* adapter does not declare `isCellReadOnly`; omit for silence.
|
|
312
|
+
* 読み取り専用拒否通知 (v0.10)。拒否されたジェスチャ毎に 1 回発火 — adapter は呼ばれて
|
|
313
|
+
* いないため EditRejection でも操作上限でもない。`isCellReadOnly` 未宣言では発火せず、
|
|
314
|
+
* 省略時は通知なし。
|
|
315
|
+
*/
|
|
316
|
+
onReadOnlyRejected?: (rejection: ReadOnlyRejection) => void
|
|
317
|
+
/**
|
|
318
|
+
* AT announcement text for a read-only refusal (v0.10), routed through the live region;
|
|
319
|
+
* omit for silence (no built-in strings). Inherits the announcement debounce + rAF collapse,
|
|
320
|
+
* which is what keeps autorepeat / IME refusal storms from spamming the live region.
|
|
321
|
+
* 読み取り専用拒否の AT 読み上げ文言 (v0.10) — live region 経由。省略で無音 (組み込み文言
|
|
322
|
+
* なし)。読み上げのデバウンス + rAF 合流を継承し、autorepeat / IME の拒否連打でも live
|
|
323
|
+
* region は溢れない。
|
|
324
|
+
*/
|
|
325
|
+
formatReadOnlyAnnouncement?: (rejection: ReadOnlyRejection) => string
|
|
326
|
+
/**
|
|
327
|
+
* Paste outcome summary (v0.10 plan §3.1): the menu channel mirrors `paste(options)`'s
|
|
328
|
+
* resolve value 1:1 (read refusals and the write-time re-gate included); the event channel
|
|
329
|
+
* fires once per gesture that reaches the paste pipeline — the drag-freeze consumption and a
|
|
330
|
+
* null `clipboardData` stay deliberately silent, and an empty grid attaches no listener at
|
|
331
|
+
* all. Cause-specific callbacks (`onClipboardUnavailable` / `onOperationRejected` /
|
|
332
|
+
* `onReadOnlyRejected`) fire FIRST — `"rejected"` can be the trailing summary of one of
|
|
333
|
+
* them. Omit = never fired.
|
|
334
|
+
* 貼り付け結果の要約 (v0.10 計画 §3.1)。menu チャネルは `paste(options)` の resolve 値の
|
|
335
|
+
* 1:1 鏡 (読み拒否・書込時再ゲート含む)、event チャネルは貼り付けパイプラインに到達した
|
|
336
|
+
* ゲスチャ毎 1 回 — ドラッグ凍結消費と `clipboardData` null は意図的無言、空グリッドは
|
|
337
|
+
* リスナ自体が無い。原因特定系 callback が先に発火し、`"rejected"` はその後追いの要約に
|
|
338
|
+
* なり得る。省略時は発火なし。
|
|
339
|
+
*/
|
|
340
|
+
onPasteResolved?: (outcome: PasteOutcome, channel: ClipChannel) => void
|
|
258
341
|
/** Change notice for out-of-package writes (undo / collaboration); each increment repaints visible cells (§6.9-2). / パッケージ外経路のデータ変更通知。 */
|
|
259
342
|
dataVersion?: number
|
|
260
343
|
/** Operation cell-count ceiling, default 100,000 (ADR-12). / 操作セル数上限。 */
|
|
@@ -281,12 +364,19 @@ export type SpreadsheetProps = {
|
|
|
281
364
|
* AT announcement text for the active cell; omit for silence (no built-in strings). `text`
|
|
282
365
|
* is the RAW canonical getCellText; `displayText` is what a sighted user sees (the
|
|
283
366
|
* getCellFormat-formatted string — equals `text` when the cell has no format), so AT can
|
|
284
|
-
* hear "87.0%" for a cell whose raw value is "0.87".
|
|
367
|
+
* hear "87.0%" for a cell whose raw value is "0.87". `readOnly` (v0.10) is
|
|
368
|
+
* declaration-gated three-valued: the key is ABSENT when the adapter does not declare
|
|
369
|
+
* `isCellReadOnly` (payload byte-identical to v0.9), `false` = declared editable, `true` =
|
|
370
|
+
* declared read-only — the navigation-time read-only channel (DOM focus stays on the sink,
|
|
371
|
+
* so `aria-readonly` is inert during Arrow navigation).
|
|
285
372
|
* AT 向け読み上げ文言 (省略で無音)。`text` は素の正準 getCellText、`displayText` は目視と同じ
|
|
286
373
|
* 表示文字列 (getCellFormat 整形後 — 書式なしセルでは text と同値)。素値 "0.87" の表示
|
|
287
|
-
* "87.0%"
|
|
374
|
+
* "87.0%" をそのまま読み上げられる。`readOnly` (v0.10) は宣言ゲートの 3 値: `isCellReadOnly`
|
|
375
|
+
* 未宣言はキー自体なし (payload は v0.9 とバイト同一)、`false` = 宣言済み編集可、`true` =
|
|
376
|
+
* 宣言済み読み取り専用 — 矢印航行時の読み取り専用チャネル (フォーカスはシンク常駐のため
|
|
377
|
+
* `aria-readonly` は航行中の AT に届かない)。
|
|
288
378
|
*/
|
|
289
|
-
formatCellAnnouncement?: (ctx: { row: number; col: number; text: string; displayText: string; columnName: string }) => string
|
|
379
|
+
formatCellAnnouncement?: (ctx: { row: number; col: number; text: string; displayText: string; columnName: string; readOnly?: boolean }) => string
|
|
290
380
|
/** AT announcement for the selection extent ("A1 to C3, 3 rows 3 cols"); omit for silence. / AT 向け選択範囲読み上げ。 */
|
|
291
381
|
formatSelectionAnnouncement?: (ctx: { range: CellRange; anchor: CellCoord; active: CellCoord; rangeRowCount: number; rangeColCount: number; rangeCount: number }) => string
|
|
292
382
|
/**
|
|
@@ -420,6 +510,21 @@ const ZOOM_MAX = 4
|
|
|
420
510
|
* fail-fast — Math.min/max は NaN を素通しし、全 ÷z 境界の汚染と同値バウンド無効化 (NaN 同士は
|
|
421
511
|
* 不一致) による onZoomChange(NaN) 連射を招くため。
|
|
422
512
|
*/
|
|
513
|
+
/**
|
|
514
|
+
* Validates one overscan prop and returns it unchanged (fail-fast — the engine clamps invalid
|
|
515
|
+
* overscan silently, so rejection is owned by this surface). Undefined is the declared absence
|
|
516
|
+
* signal (engine default) and is exempt. Throws RangeError on a non-integer or negative value.
|
|
517
|
+
* overscan prop 1 つの検証 (値はそのまま返す。fail-fast — エンジンは不正 overscan を無言
|
|
518
|
+
* クランプするため拒否は本層所有)。undefined は宣言不在信号 (エンジン既定) で対象外。非整数・
|
|
519
|
+
* 負値は RangeError。
|
|
520
|
+
*/
|
|
521
|
+
const validateOverscan = (name: "overscanRows" | "overscanCols", value: number | undefined): number | undefined => {
|
|
522
|
+
if (value !== undefined && !(Number.isInteger(value) && value >= 0)) {
|
|
523
|
+
throw new RangeError(`[spreadsheet] ${name} must be a non-negative integer (got ${value})`)
|
|
524
|
+
}
|
|
525
|
+
return value
|
|
526
|
+
}
|
|
527
|
+
|
|
423
528
|
const clampZoom = (value: number): number => {
|
|
424
529
|
if (!Number.isFinite(value)) {
|
|
425
530
|
throw new RangeError(`[spreadsheet] zoom must be a finite number (got ${value})`)
|
|
@@ -444,6 +549,17 @@ const stepZoom = (value: number, direction: 1 | -1): number => (direction > 0 ?
|
|
|
444
549
|
type SpreadsheetTestProps = {
|
|
445
550
|
/** Test-only CellView re-render counter; never passed in production. / テスト専用カウンタ。 */
|
|
446
551
|
__onCellRender?: ProfilerOnRenderCallback
|
|
552
|
+
/**
|
|
553
|
+
* Test-only virtual-keyboard raise spy (v0.10 plan §6.4 / RC-G2t): called synchronously by
|
|
554
|
+
* the single `raiseVirtualKeyboard` funnel, so spy count ≡ raise executions — a React-batched
|
|
555
|
+
* raise-then-lower mutates no DOM (`inputmode` observer-blind) yet still counts. Never
|
|
556
|
+
* passed in production (zero calls when unset — the `__onCellRender` conduct).
|
|
557
|
+
* テスト専用の仮想キーボード招待スパイ (v0.10 計画 §6.4 / RC-G2t)。単一漏斗
|
|
558
|
+
* `raiseVirtualKeyboard` が同期で呼ぶため、計数 ≡ raise 実行 — React バッチの
|
|
559
|
+
* raise-then-lower は DOM 無変異 (`inputmode` の観測は盲目) でも計数には映る。production
|
|
560
|
+
* では渡さない (未指定 = 呼出しゼロ — `__onCellRender` と同じ品行)。
|
|
561
|
+
*/
|
|
562
|
+
__onVirtualKeyboardRaise?: () => void
|
|
447
563
|
}
|
|
448
564
|
|
|
449
565
|
const DEFAULT_ROW_HEIGHT = 24
|
|
@@ -554,6 +670,10 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
554
670
|
frozenRows = 0,
|
|
555
671
|
frozenTrailingColumns = 0,
|
|
556
672
|
frozenTrailingRows = 0,
|
|
673
|
+
tapScrollCircleOptions,
|
|
674
|
+
overscanRows,
|
|
675
|
+
overscanCols,
|
|
676
|
+
background,
|
|
557
677
|
renderCell,
|
|
558
678
|
onSelectionChange,
|
|
559
679
|
onCellContextMenu,
|
|
@@ -562,6 +682,8 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
562
682
|
onCopyMultiRange,
|
|
563
683
|
onOperationRejected,
|
|
564
684
|
onEditRejected,
|
|
685
|
+
onReadOnlyRejected,
|
|
686
|
+
formatReadOnlyAnnouncement,
|
|
565
687
|
dataVersion,
|
|
566
688
|
maxOperationCells = DEFAULT_MAX_OPERATION_CELLS,
|
|
567
689
|
enableFillHandle = true,
|
|
@@ -584,6 +706,7 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
584
706
|
onValidationRejected,
|
|
585
707
|
onPasteSpecialRequest,
|
|
586
708
|
onClipboardUnavailable,
|
|
709
|
+
onPasteResolved,
|
|
587
710
|
now = Date.now,
|
|
588
711
|
zoom,
|
|
589
712
|
defaultZoom,
|
|
@@ -591,6 +714,7 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
591
714
|
testId,
|
|
592
715
|
className,
|
|
593
716
|
__onCellRender,
|
|
717
|
+
__onVirtualKeyboardRaise,
|
|
594
718
|
}: SpreadsheetProps & SpreadsheetTestProps,
|
|
595
719
|
ref,
|
|
596
720
|
): ReactNode {
|
|
@@ -1098,6 +1222,28 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
1098
1222
|
[],
|
|
1099
1223
|
)
|
|
1100
1224
|
|
|
1225
|
+
/**
|
|
1226
|
+
* The single read-only refusal funnel (v0.10 plan §6.4 — the SSOT of RC-ANN / RC-B): every
|
|
1227
|
+
* gate (G1..G16) delivers its rejection here, which relays the callback AND the announcement
|
|
1228
|
+
* (`formatReadOnlyAnnouncement` → `announce()` — the debounce is inherited). Both props
|
|
1229
|
+
* omitted = silence = byte-identical; the read-only refusal never rides `onEditRejected` /
|
|
1230
|
+
* `onOperationRejected` (channel purity).
|
|
1231
|
+
* 読み取り専用拒否の単一漏斗 (v0.10 計画 §6.4 — RC-ANN / RC-B の SSOT)。全ゲート (G1..G16)
|
|
1232
|
+
* の拒否はここへ集まり、callback と読み上げ (`formatReadOnlyAnnouncement` → `announce()` —
|
|
1233
|
+
* デバウンス継承) の両方を運ぶ。両 prop 省略 = 無音 = バイト恒等。読み取り専用拒否を
|
|
1234
|
+
* `onEditRejected` / `onOperationRejected` へ流用しない (チャネル純度)。
|
|
1235
|
+
*/
|
|
1236
|
+
const rejectReadOnly = useCallback(
|
|
1237
|
+
(rejection: ReadOnlyRejection) => {
|
|
1238
|
+
onReadOnlyRejected?.(rejection)
|
|
1239
|
+
// 読み上げは prop 宣言時だけ (省略 = 無音 — 組み込み文言なしの家訓)
|
|
1240
|
+
if (formatReadOnlyAnnouncement !== undefined) {
|
|
1241
|
+
announce(formatReadOnlyAnnouncement(rejection))
|
|
1242
|
+
}
|
|
1243
|
+
},
|
|
1244
|
+
[onReadOnlyRejected, formatReadOnlyAnnouncement, announce],
|
|
1245
|
+
)
|
|
1246
|
+
|
|
1101
1247
|
/** The 3-tier announcement rule (§6.9-9, evaluated top-down). / 3 段機械判定 (上から順)。 */
|
|
1102
1248
|
const evaluateAnnouncement = useCallback(
|
|
1103
1249
|
(prev: SelectionState, next: SelectionState) => {
|
|
@@ -1115,7 +1261,18 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
1115
1261
|
const raw = adapter.getCellText(a.row, a.col)
|
|
1116
1262
|
// displayText は目視と同一の整形後文字列 (塗り書式 = base ⊕ CF — getCellFormat 未宣言は raw と同値)
|
|
1117
1263
|
const painted = adapter.getCellFormat === undefined ? undefined : paintedFormatOf(a.row, a.col, () => raw)
|
|
1118
|
-
|
|
1264
|
+
// S2 (v0.10 §6.4): readOnly キーは宣言ゲート — isCellReadOnly 未宣言はキー自体を出さず
|
|
1265
|
+
// payload は v0.9 とバイト同一、宣言時は正負両真理のライブ回答 (RC-S2)
|
|
1266
|
+
announce(
|
|
1267
|
+
formatCellAnnouncement({
|
|
1268
|
+
row: a.row,
|
|
1269
|
+
col: a.col,
|
|
1270
|
+
text: raw,
|
|
1271
|
+
displayText: formatCellValue(raw, painted === undefined ? undefined : painted.value),
|
|
1272
|
+
columnName: columnIndexToName(a.col),
|
|
1273
|
+
...(readOnlyDeclared(adapter) ? { readOnly: isReadOnlyCell(adapter, a.row, a.col) } : {}),
|
|
1274
|
+
}),
|
|
1275
|
+
)
|
|
1119
1276
|
}
|
|
1120
1277
|
}
|
|
1121
1278
|
// (1) 単一 1×1 レンジ → セル読み上げ (素のクリック / 矢印移動)
|
|
@@ -1229,14 +1386,37 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
1229
1386
|
// OnTextInputTypeChanged で進行中の変換を強制終了する: 未編集 → IME 1 文字 → Enter が
|
|
1230
1387
|
// 落ちた利用者報告の根因 — セッション基準の切替は compositionstart の中で属性を変えていた)
|
|
1231
1388
|
const [virtualKeyboard, setVirtualKeyboard] = useState(false)
|
|
1389
|
+
/**
|
|
1390
|
+
* The ONE invitation-raising funnel (v0.10 plan §6.4 / RC-G2t): `setVirtualKeyboard(true)`
|
|
1391
|
+
* has exactly this production site, so the test seam's spy count ≡ raise executions by
|
|
1392
|
+
* construction — a React-batched raise-then-lower mutates no DOM yet still counts. The two
|
|
1393
|
+
* LOWERING sites (`onRootBlur`, the non-mouse root-capture pointerdown) call
|
|
1394
|
+
* `setVirtualKeyboard(false)` directly. The uniqueness invariant is machine-sealed by the
|
|
1395
|
+
* side-(iv) census of `readOnlyCensus.spec.ts` (a second raise site cannot land silently).
|
|
1396
|
+
* 招待 raise の単一漏斗 (v0.10 計画 §6.4 / RC-G2t)。`setVirtualKeyboard(true)` の production
|
|
1397
|
+
* 出現はここだけ — テストシームのスパイ計数 ≡ raise 実行が構成的に成立し、React バッチの
|
|
1398
|
+
* raise-then-lower も DOM 無変異のまま計数に映る。下ろし 2 サイト (`onRootBlur` / 非 mouse の
|
|
1399
|
+
* root capture pointerdown) は `setVirtualKeyboard(false)` を直呼びのまま。単一性の不変条件は
|
|
1400
|
+
* `readOnlyCensus.spec.ts` の側 (iv) census が機械封印 (第 2 raise サイトは無音で着地できない)。
|
|
1401
|
+
*/
|
|
1402
|
+
const raiseVirtualKeyboard = useCallback(() => {
|
|
1403
|
+
__onVirtualKeyboardRaise?.()
|
|
1404
|
+
setVirtualKeyboard(true)
|
|
1405
|
+
}, [__onVirtualKeyboardRaise])
|
|
1232
1406
|
const onRequestEdit = useCallback(
|
|
1233
|
-
(
|
|
1234
|
-
//
|
|
1235
|
-
//
|
|
1236
|
-
|
|
1407
|
+
(cell: CellCoord, caretIndex?: number) => {
|
|
1408
|
+
// G2 (v0.10 §6.4 / 設計 ADR-29 v3.1 — 招待前置が規範): raise は startEdit より先に走る
|
|
1409
|
+
// ため、読み取り専用セルでは招待自体をスキップする。G1 だけでは OSK が拒否より先に
|
|
1410
|
+
// 上がってしまう (キーボード無し端末のフラッシュ)。skip でも startEdit は呼ぶ —
|
|
1411
|
+
// 拒否発火サイトは G1 の 1 箇所 (SSOT)。能力未宣言は従来どおり無条件 raise (バイト恒等)
|
|
1412
|
+
if (!isReadOnlyCell(adapter, cell.row, cell.col)) {
|
|
1413
|
+
// ポインタで開くエディタだけが仮想キーボードを招待する (キーボード無し端末で開ける唯一の経路)。
|
|
1414
|
+
// 変換は進行中でない (dblclick 押下面が先に requestCommit 済み) — 属性変更は安全
|
|
1415
|
+
raiseVirtualKeyboard()
|
|
1416
|
+
}
|
|
1237
1417
|
startEdit("edit", caretIndex)
|
|
1238
1418
|
},
|
|
1239
|
-
[startEdit],
|
|
1419
|
+
[adapter, raiseVirtualKeyboard, startEdit],
|
|
1240
1420
|
)
|
|
1241
1421
|
const getClipRef = useRef<(() => ClipInfo | null) | null>(null)
|
|
1242
1422
|
const getClip = useCallback(() => getClipRef.current?.() ?? null, [])
|
|
@@ -1595,6 +1775,8 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
1595
1775
|
commitEdits,
|
|
1596
1776
|
maxOperationCells,
|
|
1597
1777
|
onOperationRejected,
|
|
1778
|
+
// v0.10: G7 の拒否漏斗 (§6.4)
|
|
1779
|
+
rejectReadOnly,
|
|
1598
1780
|
writeFillPreview: onFillFrame,
|
|
1599
1781
|
repaintOverlay,
|
|
1600
1782
|
xAutoScroller: fillXAutoScroller,
|
|
@@ -1685,6 +1867,8 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
1685
1867
|
rearmSink,
|
|
1686
1868
|
discardClip,
|
|
1687
1869
|
isDragging: isAnyDragging,
|
|
1870
|
+
// v0.10: G1 / G16 の拒否漏斗 (callback + 読み上げの SSOT — §6.4)
|
|
1871
|
+
rejectReadOnly,
|
|
1688
1872
|
// Excel 同様、編集中もセルの水平揃えを保つ (数値セルは右揃えのまま編集)。getCellFormat 未宣言は
|
|
1689
1873
|
// null = v0.1 恒等 (textarea の text-align を触らない)。宣言時は塗り書式 (base ⊕ CF) で判定し
|
|
1690
1874
|
// text は遅延 1 回読み (v0.8 §4.2.13)
|
|
@@ -1746,6 +1930,9 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
1746
1930
|
onClipChange,
|
|
1747
1931
|
onClipboardUnavailable,
|
|
1748
1932
|
onEditRejected,
|
|
1933
|
+
// v0.10: G4 / G5 / G6a / G6b の拒否漏斗 (§6.4) + 貼り付け結果の要約 (§3.1)
|
|
1934
|
+
rejectReadOnly,
|
|
1935
|
+
onPasteResolved,
|
|
1749
1936
|
})
|
|
1750
1937
|
getClipRef.current = clipboard.getClip
|
|
1751
1938
|
discardClipRef.current = clipboard.discardClip
|
|
@@ -1775,6 +1962,8 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
1775
1962
|
onHyperlinkOpen,
|
|
1776
1963
|
anchors: popoverAnchorsRef,
|
|
1777
1964
|
repaintOverlay,
|
|
1965
|
+
// v0.10: G14a / G14b / G14c の拒否漏斗 (§6.4)
|
|
1966
|
+
rejectReadOnly,
|
|
1778
1967
|
})
|
|
1779
1968
|
popoversRef.current = popovers
|
|
1780
1969
|
openPopoverRef.current = popovers.open
|
|
@@ -1834,6 +2023,8 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
1834
2023
|
announce,
|
|
1835
2024
|
formatCommandAnnouncement,
|
|
1836
2025
|
keyboardCellRect,
|
|
2026
|
+
// v0.10: G8–G13 / G15 の拒否漏斗 (§6.4)
|
|
2027
|
+
rejectReadOnly,
|
|
1837
2028
|
})
|
|
1838
2029
|
commandsRef.current = commands
|
|
1839
2030
|
executeCommandRef.current = commands.executeCommand
|
|
@@ -1868,6 +2059,8 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
1868
2059
|
commitEdits,
|
|
1869
2060
|
maxOperationCells,
|
|
1870
2061
|
onOperationRejected,
|
|
2062
|
+
// v0.10: G3 (Delete / Backspace) の拒否漏斗 (§6.4)
|
|
2063
|
+
rejectReadOnly,
|
|
1871
2064
|
getClip,
|
|
1872
2065
|
discardClip,
|
|
1873
2066
|
isDragging: isDraggingAny,
|
|
@@ -2350,7 +2543,8 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
2350
2543
|
}
|
|
2351
2544
|
|
|
2352
2545
|
// live region は読み上げ prop のどれか 1 つでも宣言されたときだけ描く (コマンド通知も同じ領域へ)
|
|
2353
|
-
|
|
2546
|
+
// v0.10: formatReadOnlyAnnouncement も live region の存在条件 (拒否読み上げは announce() 経由 — §6.4)
|
|
2547
|
+
const hasAnnouncement = formatCellAnnouncement !== undefined || formatSelectionAnnouncement !== undefined || formatCommandAnnouncement !== undefined || formatReadOnlyAnnouncement !== undefined
|
|
2354
2548
|
|
|
2355
2549
|
return (
|
|
2356
2550
|
// biome-ignore lint/a11y/useSemanticElements: fieldset はフォーム意味論であり不適。role="group" は Escape 脱出の着地点に現在地名を与えるための選択 (§6.9-7 / §10-4)
|
|
@@ -2437,6 +2631,10 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
|
|
|
2437
2631
|
frozenRows={frozenRows}
|
|
2438
2632
|
frozenTrailingColumns={frozenTrailingColumns}
|
|
2439
2633
|
frozenTrailingRows={frozenTrailingRows}
|
|
2634
|
+
tapScrollCircleOptions={tapScrollCircleOptions}
|
|
2635
|
+
overscanRows={validateOverscan("overscanRows", overscanRows)}
|
|
2636
|
+
overscanCols={validateOverscan("overscanCols", overscanCols)}
|
|
2637
|
+
background={background}
|
|
2440
2638
|
renderCell={renderCell}
|
|
2441
2639
|
onScroll={onScroll}
|
|
2442
2640
|
onRangeChange={onRangeChange}
|