I have a component which has a webview that uses quill.js to create a rich text editor .the component itself part of another view which has multiple elements which places this view to the bottom when I tap the webview which in turn makes the quill editor editable.Ideally due adjustResize
the view should scroll up as i am also using imePadding() but for webview it doesn't work
here is my editor
@SuppressLint("SetJavaScriptEnabled", "ClickableViewAccessibility")
@Composable
fun UpdateEditor(
htmlString: String,
modifier: Modifier = Modifier,
onTextChangeCallback: (Int, String) -> Unit,
enableButton: (Boolean) -> Unit,
projectId: MutableInt,
date: MutableState<Date>,
hideKeyboardTrigger: Boolean,
update: MutableState<Update?>
) {
val context = LocalContext.current
val webView = remember {
WebView(context).apply {
settings.apply {
javaScriptEnabled = true
domStorageEnabled = true
defaultTextEncodingName = "UTF-8";
}
addJavascriptInterface(
DailyUpdateScriptInterface(
onTextChangeCallback,
projectId,
enableButton
),
"Editor"
)
isVerticalScrollBarEnabled = false
isHorizontalScrollBarEnabled = false
overScrollMode = WebView.OVER_SCROLL_ALWAYS
viewTreeObserver.addOnGlobalLayoutListener {
if (!isKeyboardShown(rootView)) {
evaluateJavascript("document.activeElement.blur();", null)
}
}
webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
// Handle page loading finished
val placeholder = "Add Update ..."
val jsScript = """
javascript:(function() {
window.setQuillPlaceholder('$placeholder');
quill.setSelection(selection);
})()
"""
view?.evaluateJavascript(jsScript, null)
}
}
}
}
Box(modifier.imePadding()) {
AndroidView(
factory = { webView },
) {
// Check if the WebView is loading the URL for the first time
if (it.url == null) {
it.loadUrl("file:///android_asset/editor.html")
}
it.setOnTouchListener { v, event ->
// Handle touch events to adjust the viewport or other necessary actions
false
}
}
}
LaunchedEffect(htmlString) {
val jsScript = """
javascript:(function() {
window.setQuillContent('${htmlString.replace("\n", "<br>")}');
}
)()
"""
webView.evaluateJavascript(jsScript, null)
}
}
here is my html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<link href="https://cdn.quilljs.com/1.1.6/quill.snow.css" rel="stylesheet">
<link href='https://fonts.googleapis.com/css?family=Inter' rel='stylesheet'>
<script src="https://cdn.quilljs.com/1.1.6/quill.js"></script>
<style>
body {
font-family: 'Inter';
font-size: 14px;
margin: 0;
padding: 0;
}
#toolbar {
position: sticky;
top: 0;
display: flex;
flex-wrap: wrap;
width: 100%;
background: #ffffff;
z-index: 100;
border: none;
}
#editor-container {
overflow-y: scroll;
box-sizing: border-box; /* Include padding and border in total width/height */
}
#editor {
height: 100%;
border: none;
}
.quill .ql-snow {
border: none;
}
.quill .ql-editor {
line-height: unset;
height: 100%;
border: none;
font-family: inherit;
background-color: none;
}
.quill .ql-editor ul,
.quill .ql-editor ol {
padding-left: 0;
}
.quill .ql-tooltip {
margin-left: 140px;
z-index: 2;
}
.ql-cursor {
color: #FF5C8AFF !important;
}
.editor-text h1,
.editor-text h2,
.editor-text h3,
.editor-text p,
.editor-text blockquote {
margin-block-start: 6px;
margin-block-end: 6px;
margin-inline-start: 0;
margin-inline-end: 0;
}
.editor-text ul,
.editor-text ol {
padding-inline-start: 12px;
}
.editor-text ul li,
.editor-text ol li {
word-break: break-word;
margin: 6px 0;
font-size: 14px;
}
.editor-text p {
white-space: pre-line;
font-size: 14px;
word-break: break-word;
overflow-y: scroll;
}
/* Example media query for screens smaller than 600px */
@media (max-width: 600px) {
#editor-container {
padding: 0; /* Adjust or remove padding for smaller screens */
}
}
/* Dark mode styles */
@media (prefers-color-scheme: dark) {
body {
background: #333333;
color: white;
font-family: 'Inter';
font-size: 14px;
margin: 0;
padding: 0;
}
#toolbar {
position: sticky;
top: 0;
display: flex;
flex-wrap: wrap;
width: 100%;
background: #333333;
z-index: 100;
border: none;
}
.ql-toolbar button svg {
filter: invert(1);
}
.ql-snow .ql-picker.ql-header .ql-picker-label,
.ql-snow .ql-picker.ql-header .ql-picker-item {
color: #ffffff;
}
/* Styling the dropdown background color (optional) */
.ql-snow .ql-picker.ql-header .ql-picker-options {
background-color: #1E1E1E; /* or any other suitable dark color */
}
#editor {
color: #ffffff;
}
.ql-editor.ql-blank::before {
color: #CCCCCC; /* Choose the color that fits the standard mode best */
}
}
</style>
</head>
<body>
<div id="container">
<div id="toolbar">
<!-- Add buttons for the features you want -->
<select class="ql-header">
<option value="1">Heading 1</option>
<option value="2">Heading 2</option>
<option value="3">Heading 3</option>
<option value="">Normal</option>
</select>
<button class="ql-bold">Bold</button>
<button class="ql-italic">Italic</button>
<button class="ql-link">Link</button>
<button class="ql-list" value="ordered">Ordered List</button>
<button class="ql-list" value="bullet">Unordered List</button>
</div>
<div id="editor-container">
<div id="editor"></div>
</div>
</div>
<!-- Create the toolbar container -->
<script>
var quill = new Quill('#editor', {
modules: {
toolbar: '#toolbar'
},
placeholder: 'Enter Update...',
theme: 'snow' // or 'bubble',
});
// New function to set the editor content
let selection = quill.getSelection();
window.setQuillContent = function(content) {
var contentWithoutLineBreaks = content.replace(/<br>/g, '');
var delta = quill.clipboard.convert(content);
quill.setContents(delta, 'silent');
quill.setSelection(selection);
}
window.getQuillContent = function() {
return quill.root.innerHTML;
}
quill.on('text-change', function(delta, oldDelta, source) {
if (source == 'user') {
var htmlContent = quill.root.innerHTML;
// Replace AndroidFunction with your actual Android function for passing data to Android.
Editor.onTextChange(htmlContent);
}
});
function setQuillPlaceholder(placeholderText) {
quill.container.dataset.placeholder = placeholderText;
// Force Quill to recognize the change
if (quill.root.dataset.placeholder !== undefined) {
quill.root.dataset.placeholder = placeholderText;
}
}
document.getElementById('editor').addEventListener('click', function() {
quill.focus();
});
function setContainerHeight() {
var toolbarHeight = 40; // replace this with actual toolbar height if dynamic
var viewportHeight = window.innerHeight;
var editorContainer = document.getElementById('editor-container');
editorContainer.style.height = (viewportHeight - toolbarHeight - adjustment) + 'px';
}
setContainerHeight(); // initial set
window.addEventListener('resize', setContainerHeight);
</script>
</body>
</html>
I have tried using
val rootView = (context as? Activity)?.window?.decorView?.findViewById\<View\>. (android.R.id.content)
LaunchedEffect(rootView) {
val listener = ViewTreeObserver.OnGlobalLayoutListener {
val rect = Rect()
rootView?.getWindowVisibleDisplayFrame(rect)
val screenHeight = rootView?.height ?: 0
val keypadHeight = screenHeight - rect.bottom
if (keypadHeight > screenHeight * 0.15) {
// Keyboard is opened
listState.animateScrollToItem(0)
} else {
// Keyboard is closed
}
}
rootView?.viewTreeObserver?.addOnGlobalLayoutListener(listener)
onDispose {
rootView?.viewTreeObserver?.removeOnGlobalLayoutListener(listener)
}
}
I have added imePadding to every parent view as well as tried using
UpdateEditor(
htmlString = htmlString.value,
modifier = Modifier
.fillMaxWidth()
.verticalScroll(rememberScrollState()),
onTextChangeCallback = onTextChangeCallback,
projectId = mutableProjectId,
enableButton = { enableButton = true },
date = date,
hideKeyboardTrigger = hideKeyboardTrigger,
update = mutableUpdate
)