@@ -2,7 +2,11 @@ import { useEffect, useRef, useState, useId } from 'react';
22import { createUniver , LocaleType , mergeLocales } from '@univerjs/presets' ;
33import { UniverSheetsCorePreset } from '@univerjs/preset-sheets-core' ;
44import UniverPresetSheetsCoreEnUS from '@univerjs/preset-sheets-core/locales/en-US' ;
5- import { WorkbookEditablePermission } from '@univerjs/sheets' ;
5+ import {
6+ WorkbookEditablePermission ,
7+ WorksheetSelectProtectedCellsPermission ,
8+ WorksheetSelectUnProtectedCellsPermission ,
9+ } from '@univerjs/sheets' ;
610
711import '@univerjs/preset-sheets-core/lib/index.css' ;
812
@@ -129,12 +133,21 @@ export function PublicSheetsViewer({ content, darkMode = false }: PublicSheetsVi
129133 // Create workbook with initial data
130134 const workbook = univerAPI . createWorkbook ( workbookData ) ;
131135
132- // Set workbook to read-only mode
136+ // Set workbook to read-only mode and disable cell selection
133137 if ( workbook ) {
134138 const unitId = workbook . getId ( ) ;
135139 const permission = univerAPI . getPermission ( ) ;
136140 if ( permission && unitId ) {
141+ // Disable editing
137142 permission . setWorkbookPermissionPoint ( unitId , WorkbookEditablePermission , false ) ;
143+
144+ // Disable cell selection for all worksheets
145+ const sheets = workbook . getSheets ( ) ;
146+ sheets . forEach ( ( sheet ) => {
147+ const sheetId = sheet . getSheetId ( ) ;
148+ permission . setWorksheetPermissionPoint ( unitId , sheetId , WorksheetSelectProtectedCellsPermission , false ) ;
149+ permission . setWorksheetPermissionPoint ( unitId , sheetId , WorksheetSelectUnProtectedCellsPermission , false ) ;
150+ } ) ;
138151 }
139152 }
140153
@@ -180,6 +193,75 @@ export function PublicSheetsViewer({ content, darkMode = false }: PublicSheetsVi
180193 } ;
181194 } , [ instanceId ] ) ;
182195
196+ // Check if mobile/touch device
197+ const isMobile = typeof window !== 'undefined' &&
198+ ( 'ontouchstart' in window || navigator . maxTouchPoints > 0 ) ;
199+
200+ // Mobile readonly mode: disable pointer events and implement custom touch scroll
201+ // Based on: https://github.com/dream-num/univer/discussions/2198
202+ useEffect ( ( ) => {
203+ if ( ! isMobile || state !== 'ready' ) return ;
204+
205+ const container = containerRef . current ;
206+ const instance = viewerInstances . get ( instanceId ) ;
207+ if ( ! container || ! instance ) return ;
208+
209+ const { univerAPI } = instance . univer ;
210+ const workbook = univerAPI . getActiveWorkbook ( ) ;
211+ if ( ! workbook ) return ;
212+
213+ // Disable pointer events on the Univer container to prevent keyboard
214+ container . style . pointerEvents = 'none' ;
215+
216+ // Track touch state for scrolling
217+ let lastX = 0 ;
218+ let lastY = 0 ;
219+
220+ const handleTouchStart = ( e : TouchEvent ) => {
221+ const touch = e . touches [ 0 ] ;
222+ lastX = touch . clientX ;
223+ lastY = touch . clientY ;
224+ } ;
225+
226+ const handleTouchMove = ( e : TouchEvent ) => {
227+ const touch = e . touches [ 0 ] ;
228+ const deltaX = lastX - touch . clientX ;
229+ const deltaY = lastY - touch . clientY ;
230+ lastX = touch . clientX ;
231+ lastY = touch . clientY ;
232+
233+ // Execute scroll command
234+ try {
235+ const activeSheet = workbook . getActiveSheet ( ) ;
236+ if ( activeSheet ) {
237+ univerAPI . executeCommand ( 'sheet.operation.set-scroll' , {
238+ unitId : workbook . getId ( ) ,
239+ sheetId : activeSheet . getSheetId ( ) ,
240+ offsetX : deltaX ,
241+ offsetY : deltaY ,
242+ } ) ;
243+ }
244+ } catch {
245+ // Ignore scroll errors
246+ }
247+ } ;
248+
249+ // Add touch handlers to the parent (which still has pointer events)
250+ const parent = container . parentElement ;
251+ if ( parent ) {
252+ parent . addEventListener ( 'touchstart' , handleTouchStart , { passive : true } ) ;
253+ parent . addEventListener ( 'touchmove' , handleTouchMove , { passive : true } ) ;
254+ }
255+
256+ return ( ) => {
257+ container . style . pointerEvents = '' ;
258+ if ( parent ) {
259+ parent . removeEventListener ( 'touchstart' , handleTouchStart ) ;
260+ parent . removeEventListener ( 'touchmove' , handleTouchMove ) ;
261+ }
262+ } ;
263+ } , [ isMobile , state , instanceId ] ) ;
264+
183265 if ( state === 'error' ) {
184266 return (
185267 < div className = "flex h-full w-full items-center justify-center bg-red-50 dark:bg-red-900/20" >
@@ -217,6 +299,16 @@ export function PublicSheetsViewer({ content, darkMode = false }: PublicSheetsVi
217299 visibility: hidden !important;
218300 pointer-events: none !important;
219301 }
302+
303+ /* Mobile readonly mode - disable all pointer events on Univer internals */
304+ @media (pointer: coarse) {
305+ .sheets-viewer-container .univer-app,
306+ .sheets-viewer-container .univer-container,
307+ .sheets-viewer-container [class*="univer-"],
308+ .sheets-viewer-container canvas {
309+ pointer-events: none !important;
310+ }
311+ }
220312 ` } </ style >
221313 < div className = { `sheets-viewer-container relative h-full w-full overflow-hidden` } >
222314 { state === 'loading' && (
0 commit comments