I'm trying to translate some CSS to ScalaCSS and I can't quite figure out how to encode a pattern of "class subclass". Here is a specific example:
.navbar-top-links {
margin-right: 0;
}
.navbar-top-links li {
display: inline-block;
}
.navbar-top-links li:last-child {
margin-right: 15px;
}
In the quickstart you can see the use of & to define child styles:
".navbar-top-links" - (
marginRight(0),
&("li") - (
display.inlineBlock,
&.lastChild -
marginRight(15 px)
)
)
Related
I want the users of my app to be able to select text (words, but also whole sentences) in a WebViewWidget, and have an audio of that text played to them (via tts). I only want this to work with u̲n̲d̲e̲r̲l̲i̲n̲e̲d̲ text (or text visually highlighted and set apart from the rest in any other way).
This is part of my Widget tree, where I have tested a few gestureRecognizers (please read the comments):
child: GestureDetector(
//gestureRecognizers of WebViewWidget don't do anything without this GestureDetector,
//see: https://stackoverflow.com/questions/58811375/tapgesturerecognizer-not-working-in-flutter-webview#answer-59298134
onTap: () => print('this line doesnt get printed'),
child: WebViewWidget(
controller: _controller,
gestureRecognizers: {
Factory<LongPressGestureRecognizer>(() => LongPressGestureRecognizer()
..onLongPress = () {
print('onLongPress');
}
..onLongPressStart = (LongPressStartDetails details) {
print('onLongPressStart, ${details.globalPosition}, ${details.localPosition}');
}),
Factory<TapGestureRecognizer>(() => TapGestureRecognizer()
..onTap = () {
print('onTap'); //this doesn't get printed at all
}
..onTapDown = (TapDownDetails details) {
//comment: this doesn't get printed unless I tap for a little long time (not as long as longPress)
print('onTapDown, details: $details');
/*TODO: I want to get a text selection and play audio (with tts)
String selectedText = ...?
TTSHelper.speak(text: selectedText);
*/
}),
},
),
),
Another approach to a possible solution for what I want
I have an initialization function, where I take html (without the html, body & head tags...) from firestore and wrap it in the html, body & head tags along with some custom CSS.
If there is no way to achieve my goal with Dart code, maybe there is a way for me to add JavaScript to the html, so that underlined words/sentences can be tapped to play audio via tts, or at least send that text over to my Dart code somehow?
String _prepareHTML(String content) {
return """<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {background-color: ${getCSSColor(context, CustomColors.mainAppBG)};}
h1, h2, h3, h4, h5, h6 {color: ${getCSSColor(context, CustomColors.blueWhiteText)};}
p {color: ${getCSSColor(context, CustomColors.text)};}
table {
width: 100%;
table-layout: fixed;
word-wrap: break-word;
}
table, tr, th, td {
color: ${getCSSColor(context, CustomColors.text)};
border: 1px solid ${getCSSColor(context, CustomColors.text)};
border-collapse: collapse;
}
th, td {
padding: 6px 12px;
font-weight: bold;
font-size: 1.25em;
}
</style>
</head>
<body>
$content
</body>
</html>""";
}
I think you might be able to achieve what you want by using some javascript and a javascriptChannel on the WebViewWidget. General idea below:
var controller = WebViewController();
controller.addJavascriptChannel(
JavascriptChannel(
name: 'underlinedTextSelectChannel',
onMessageReceived: (message) {
var data = json.decode(message.message);
var text = data.text as String;
TTSHelper.speak(text: selectedText);
}
)
)
...
var html = """
<html>
...
<body>
$content
# Within content:
This is some text <p class="underlined-text">Say this!</p> Don't say this.
<script>
var underlinedTexts = document.querySelectorAll('.underlined-text');
for(var t of underlinedTexts) {
t.addEventListener('onClick', (e) => {
underlinedTextSelectChannel.postMessage(JSON.stringify({
text: e.target.innerText
}))
})
}
</script>
</body>
</html>
"""
For example:
one
two
three
four
five
six
seven
eight
nine
ten
eleven
twelve
to
one two three
four five six
seven eight nine
ten eleven twelve
I couldn't figure out how to do this and was only able to do vice versa on vscode.
Not a shortcut, but you can easily do such job with find ans replace.
Ctrl+H
Find what: (\w+)\R(\w+)\R(\w+)
Replace with: $1 $2 $3
CHECK Wrap around
CHECK Regular expression
Replace all
Explanation:
(\w+) # group 1, 1 or more word characters
\R # any kind of linebreak
(\w+) # group 2, 1 or more word characters
\R
\R # any kind of linebreak
Screenshot (before):
Screenshot (after):
Not VS Code, but you can do that using this snippet:
Just bookmark this answer if you need it again (or copy and paste the snippet info into an HTML file on your device). Also, I think the "Copy to clipboard" button doesn't work because the snippet runs in a cross-origin iframe, but it should work in a same-origin context.
function splitWordsPerLine (text, wpl = 1) {
let result = '';
wpl = wpl < 1 ? 1 : wpl;
let count = wpl;
for (const word of text.split(/\s+/)) {
count -= 1;
let line = word;
if (count === 0) {
line += '\n';
count = wpl;
}
else line += ' ';
result += line;
}
return result.trim();
}
function getWPL (numberInput) {
if (!numberInput) return 1;
const wpl = parseInt(numberInput.value, 10);
return Number.isNaN(wpl) ? 1 : wpl;
}
function handleInput (event) {
const wpl = getWPL(event.target);
const textInput = document.getElementById('text');
if (!textInput) return;
textInput.value = splitWordsPerLine(textInput.value, wpl);
}
async function handleClick (event) {
let message = 'Copying failed 😭';
const textInput = document.getElementById('text');
try {
if (!textInput) throw new Error('No input found');
await navigator.clipboard.writeText(textInput.value);
message = 'Text copied ✅';
}
catch {}
textInput?.select();
const setText = str => event.target.textContent = str;
setText(message);
setTimeout(() => setText('Copy to clipboard'), 1500);
}
function handlePaste (event) {
const text = event.clipboardData?.getData('text');
if (!text) return;
const wpl = getWPL(document.getElementById('wpl'));
event.target.value = splitWordsPerLine(text, wpl);
event.preventDefault();
}
document.getElementById('wpl')?.addEventListener('input', handleInput);
document.getElementById('copy')?.addEventListener('click', handleClick);
document.getElementById('text')?.addEventListener('paste', handlePaste);
html {
box-sizing: border-box;
height: 100%;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
font-family: sans-serif;
height: 100%;
margin: 0;
padding: 1rem;
}
.container {
display: flex;
gap: 0.5rem;
}
.container.vertical {
flex-direction: column;
height: 100%;
}
#copy {
background-color: black;
border: 0;
color: white;
display: inline-flex;
align-items: center;
font-size: 1rem;
padding: 0.5rem;
}
#wpl, #text {
border: 1px solid;
font-family: monospace;
padding: 0.5rem;
}
#wpl {
font-size: 1.5rem;
width: 5rem;
}
#text {
font-size: 1rem;
height: 100%;
width: 100%;
resize: none;
white-space: pre;
}
<div class="container vertical">
<div class="container">
<input id="wpl" type="number" min="1" step="1" value="1" />
<button id="copy">Copy to clipboard</button>
</div>
<textarea id="text" rows="0" cols="0" placeholder="Select number of words per line, then paste your text here"></textarea>
</div>
You can use extension Select By and the command selectby.lineNr
Place the cursor on the first line
execute command: Place cursor based on line number, uses boolean expression
enter expression: c+3k to place a cursor every 3 lines
maybe expression: c+3k && n<50 to limit the end line to use
now use End Space Delete as often as needed
press Esc to exit Multi Cursor Mode
On styled-components we can have something like this
import syled, { css } from 'styled-components';
const StyledButton = styled.button<StyledButtonProps>`
display: flex;
${textStyles.styledButton}
${props =>
props.disabled &&
css`
opacity: 0.5;
`}
`
I'm trying to use MUI's styled but can't find how to conditionally add the opacity property based on the props passed to the button. I'm trying something like this, but don't want to define the default value of every conditional styles as on opacity here (since there could be a LOT of conditional properties based on the value of a passed prop)
import { styled } from '#material-ui/core/styles';
// eslint-disable-next-line #typescript-eslint/no-unused-vars
const StyledMUIButton = styled(({ disabled, disclosure, whiteButton, fullWidth, noMarginTop, ...rest }: StyledButtonProps) => (
<button {...rest} />
))({
display: 'flex',
...textStyles.button,
opacity: (props: StyledButtonProps) => props.disabled ? 0.5 : 1
})
Another case would be something like this in styled-components, how can be that applied to MUI's styled?
${props =>
props.whiteButton
? css`
background-color: transparent;
border: none;
${textStyles.styledLink}
`
: css`
&:focus {
background: transparent;
border: 1px solid ${colors.textLink};
color: ${colors.textLink};
}
`}
I ended up using makeStyles the following way
const useStyles = makeStyles({
button: (props: StyledButtonProps) => ({
display: 'flex',
...textStyles.button,
...props.disabled && { opacity: 0.5 },
})
})
I am trying to learn how to use the interact.js library and I cant get the resizing example to be draggable. I can resize the div ".resize-drag" but I don´t know how to get it draggable. Can anyone tell me is wrong with my code?
This code is only so that I can learn to implement the resize example provided at http://interactjs.io/ So far I´ve tried using npm instead of the script tag. When I copied the example below from the top of the interact.js website and renamed the element ".item" in the interact claus but that did not work
interact('.item').draggable({
onmove(event) {
console.log(event.pageX,
event.pageY)
}
})
I suspected it might be a syntax error so I also tried adding semicolon behind the function but that didn´t seem to be the problem. Please have a look at my entire code below to see what I have done wrong.
<!DOCTYPE html>
<!--
To change this license header, choose License Headers in Project
Properties.
To change this template file, choose Tools | Templates
and open the template in the editor.
-->
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
.resize-drag {
background-color: #29e;
color: white;
font-size: 20px;
font-family: sans-serif;
border-radius: 8px;
padding: 20px;
margin: 30px 20px;
width: 120px;
/* This makes things *much* easier */
box-sizing: border-box;
}
.resize-container {
display: inline-block;
width: 100%;
height: 240px;
background-color: lightblue;
}
</style>
<script
src="https://unpkg.com/interactjs#1.3.4/dist/interact.min.js"></script>
<script type="text/javascript">
interact('.resize-drag').draggable({
onmove(event) {
console.log(event.pageX,
event.pageY)
}
})
interact('.resize-drag')
.draggable({
onmove: window.dragMoveListener,
restrict: {
restriction: 'parent',
elementRect: { top: 0, left: 0, bottom: 1, right: 1 }
},
})
.resizable({
// resize from all edges and corners
edges: { left: true, right: true, bottom: true, top: true },
// keep the edges inside the parent
restrictEdges: {
outer: 'parent',
endOnly: true,
},
// minimum size
restrictSize: {
min: { width: 100, height: 50 },
},
inertia: true,
})
.on('resizemove', function (event) {
var target = event.target,
x = (parseFloat(target.getAttribute('data-x')) || 0),
y = (parseFloat(target.getAttribute('data-y')) || 0);
// update the element's style
target.style.width = event.rect.width + 'px';
target.style.height = event.rect.height + 'px';
// translate when resizing from top or left edges
x += event.deltaRect.left;
y += event.deltaRect.top;
target.style.webkitTransform = target.style.transform =
'translate(' + x + 'px,' + y + 'px)';
target.setAttribute('data-x', x);
target.setAttribute('data-y', y);
target.textContent = Math.round(event.rect.width) + '\u00D7' +
Math.round(event.rect.height);
});
</script>
</head>
<body>
<div class="resize-container">
<div class="resize-drag">
Resize from any edge or corner
</div>
</div>
</body>
</html>
I want to be able to drag the div and not just resize it.
I recently struggled with that as well, and it turned out that the 'resizing' code blurbs don't have all the code. The js is missing window.dragMoveListener and dragMoveListener, which is found in the 'dragging' section.
Specifically you need to add this
function dragMoveListener (event) {
var target = event.target,
// keep the dragged position in the data-x/data-y attributes
x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx,
y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
// translate the element
target.style.webkitTransform =
target.style.transform =
'translate(' + x + 'px, ' + y + 'px)';
// update the posiion attributes
target.setAttribute('data-x', x);
target.setAttribute('data-y', y);
}
// this is used later in the resizing and gesture demos
window.dragMoveListener = dragMoveListener;
mj-font does not work in mjml.
Here is what I am trying to do in index.mjml:
<mjml>
<mj-head>
<mj-font name="testFont" href="testFont.ttf"/>
</mj-head>
<mj-body>
<mj-section>
<mj-column>
<mj-text font-family="testFont" align="center" font-size="20px">
Just a text
</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>
Here is the screenshot of the project directory:
Here is what I see in the app:
And here is what the font should look like:
What am I missing here and how could I make my font work?
Thank you.
I have found it difficult to use custom fonts using the mj-font tag. If you use a google font api it seems to work correctly.
Example using Google font
<mj-font name="Raleway" href="https://fonts.googleapis.com/css?family=Raleway" />
Taking that into consideration, I have started to using the following and has worked for me: (it goes between <mj-style></mj-style> tags)
#font-face {
font-family: testFont;
src: url(https://cdn2.hubspot.net/hubfs/199900/fonts/someFontFile.ttf)
format('truetype');
}
I would normally use a .woff file instead of a .ttf file.
Diving deeper into it, if you go to a Google font link such as https://fonts.googleapis.com/css?family=Raleway in your web browser, the contents of the file is this:
/* latin-ext */
#font-face {
font-family: 'Raleway';
font-style: normal;
font-weight: 400;
src: local('Raleway'), local('Raleway-Regular'), url(https://fonts.gstatic.com/s/raleway/v13/1Ptug8zYS_SKggPNyCMIT4ttDfCmxA.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
#font-face {
font-family: 'Raleway';
font-style: normal;
font-weight: 400;
src: local('Raleway'), local('Raleway-Regular'), url(https://fonts.gstatic.com/s/raleway/v13/1Ptug8zYS_SKggPNyC0IT4ttDfA.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
Back to your problem... Since the file that you are loading with the <mj-font> tag is a binary file, I would imagine that mjml doesn't know how to process it. If the file were a .woff font file then it might work. Perhaps they could change this in the near future. After all, it is a very useful tool.