I have this little Ionic 6 Vue app which has two menu's - left and right.
<IonSplitPane content-id="main-content">
<ion-menu menuId="left-menu" side="start" content-id="main-content" type="push">
<ion-content>
<ion-menu-toggle menu="right-menu" autoHide="false">
...
</ion-menu-toggle>
</ion-content>
</ion-menu>
<ion-menu menuId="right-menu" side="end" contentId="main-content" type="overlay" :disabled="!bookHasChildren">
<ion-content>
<ion-menu-toggle menu="right-menu" autoHide="false">
...
</ion-menu-toggle>
</ion-content>
</ion-menu>
<ion-router-outlet id="main-content" ref="ionRouter"></ion-router-outlet>
</IonSplitPane>
When selecting a book, the right menu lists sub-chapter-links. Scrolling to those sub-chapters in the main view is implemented with the [scrollIntoView][1] method.
function scrollToElement() {
try {
const elementToScrollTo = divs.value.find((element) => element.localName == scrollStore.scrollElementMain);
if (elementToScrollTo != undefined && elementToScrollTo != elementToScrollTo.offsetTop) {
// scrolling
elementToScrollTo.scrollIntoView();
}
} catch (e) {
// logging scrolling error
}
}
This works fine until user clicks on left menu links where another books is loaded.
No error logging, the scrollIntoView-functionality just doesn't work anymore.
I am not sure why this breaks the scrollIntoView functionality.
Have tried so many other options and approaches, but without any success.
Demo:
Code
is on GitHub
PS: Cannot show in code sandbox as I am using Vue with script setup and I couldn't find a free code sandbox which supports this.
Related
I am trying to create a button group where a user can choose between multiple options. react-bootstrap 2.0.0-rc.0 provides the combination ToggleButtonGroup + ToggleButton for this purpose. Unfortunately, a radio icon appears next to the button. I want to get rid of it. Below, you can find a minimal example to reproduce the radio icon.
import * as React from "react";
import {
ToggleButton,
ToggleButtonGroup,
} from "react-bootstrap";
interface OwnState {
val: boolean;
}
export default class SomeToggleOptions extends React.Component<OwnProps, OwnState> {
constructor(p: Readonly<OwnProps>) {
super(p);
this.state = { val: true }
}
setVal = (newVal: number) => {
this.setState({
val: newVal == 1
})
}
render() {
return (
<div className="p-1 text-right">
<span className="p-1">Auto Refresh:</span>
<ToggleButtonGroup
name="radio"
size="sm"
onChange={this.setVal}
value={this.state.val ? 1 : 0}
>
{radios.map((radio, idx) => {
return (
<ToggleButton
key={idx}
id={`radio-${idx}`}
variant={
this.state.val === radio.value ? "dark" : "outline-dark"
}
value={idx}
>
{radio.name}
</ToggleButton>
);
})}
</ToggleButtonGroup>
</div>
);
}
}
NOTE: I already found React-Bootstrap Toggle Button is Failing to Hide the Radio Button Circle and this is NOT working for me.
The icon seems to disappear when I use the normal ButtonGroup + Button instead. But this is not primarily an option as you don't have the radio-like "exclusive" behavior there.
I reverted to the earlier react-bootstrap version 1.6.4. This is probably not fixable (without any hacky moves, css-overwriting, or similar) and induced by react-bootstrap 2.0.0 being only a release candidate so far.
In the earlier react-bootstrap version, my code snippet worked flawless.
This appears to be a temporary issue when upgrading react-bootstrap, see my answer here on duplicate question: https://stackoverflow.com/a/72636860/8291415
Also here is the closed issue on github: https://github.com/react-bootstrap/react-bootstrap/issues/5782
I found some information on CKEditor4 on trapping a clicked tool on the CKEditor toolbar but the v5 rewrite means this no longer works:
editor.on('afterCommandExec', handleAfterCommandExec);
function handleAfterCommandExec(event)
{
var commandName = event.data.name;
// For 'bold' commmand
if (commandName == 'bold')
alert("Bold button pressed!");
}
Are there any examples of working CKEditor 5 code to detect when a tool has been clicked? I'm actually trying to trap when someone clicks Track Changes to show the changes sidebar but hide it otherwise.
Maybe this works
const command = editor.commands.get('bold')
command.on('execute', () => {
console.log('Bold has been executed')
})
I have been working on a website that has to function on both desktop and tablets. Part of the website is having three columns and being able to drag orders from column to column. Sometimes on drop, the user has to answer a few questions or change some of the data of that specific order. This happens in a pop-up window that is triggered by an #drop function (for example #drop="approved()". The method approved() then checks the status of the dropped order and shows the pop-up window).
When I am on desktop, everything works just fine. But when I switch to iPad Pro in the developer tools, nothing happens. I implemented Vue Draggable, which says to work with touch devices. In their examples I can't find anything about touch events or adding new handles for touch, so I don't know what to do now.
The dragging works just fine with touch devices, it's just the #drop function that doesn't trigger.
The dropzone (it includes a component that contains the draggables and a lot of if-statements):
<div class="col-md-4 border" #dragover.prevent #drop="approved()">
<Wachtrij class="fullHeight" :data2="opdrachtenData2"></Wachtrij>
</div>
The method:
export default {
methods: {
...
approved() {
console.log("Function approved() is being executed.")
if (this.draggingOrder.status === 5) {
this.popupGekeurd = true;
}
else if (this.draggingOrder.status === 6) {
this.popupTochGoed = true;
}
else if ([40, 52, 42,41,49,55,54].indexOf(this.draggingOrder.status) !== -1) {
this.back = true;
}
},
...
}
}
The problem seems to be that you are using native events, while the touch implementation does not (always?) use these events. It is intended that you use a draggable component with one of the events outlined in the documentation. In your case the start and end events look promising. This event has a few properties (docs), some of them being to and from.
Let's assume that we have the following code:
<draggable v-for="(zone, index) in zones" v-model="zones[index]" :class="['dropzone', `zone-${index}`]" :key="`dropzone-${index}`" :options="options" #start="start" #end="end">
<div v-for="item in zones[index]" class="dropitem" :key="`dropitem-${item.id}`">
{{ item.title }}
</div>
</draggable>
This creates a few zones, each filled with their own items. Each array item of zones is changed based on where you move each item. You can then use start to have information on when you start moving an item, and end to have information on when you stop moving an item, and where that item came from and where it ended up. The following methods show off what you can do with that in this case:
methods: {
start (event) {
console.log('start', event);
},
end (event) {
console.log('end', event);
const { from, to } = event;
if (to.className.match(/\bzone-2\b/)) {
console.log('Zone 2 has something added!')
}
if (from.className.match(/\bzone-0\b/)) {
console.log('Zone 0 had something removed!');
}
}
}
We make our dropzones with a class zone-0, zone-1 or zone-2 in this case, so we can use the class name to determine which dropzone we ended up in.
An alternative way to determine which zone was changed is to simply use a watcher. Since zones changes based on where you move items, you can simply watch a particular dropzone for changes and do things based on that.
watch: {
'zones.1': {
handler (oldZone, newZone) {
if (Array.isArray(oldZone) && Array.isArray(newZone) && oldZone.length !== newZone.length) {
console.log('Zone 1 was changed from', oldZone, 'to', newZone);
}
}
}
}
A full example can be found on codesandbox.
I have set up the aurelia-dialog plugin. It's working using the example in the GitHub readme, but the documentation doesn't explain anything about how to use it otherwise. I have a simple use case with a list page. I want to click an "add new" button, pop the modal dialog which has it's own VM. The modal contains a simple dropdown list. I need to select an item on the list and make an API call to save the data, but I can't seem to figure out how to wire up my save method with the save button on the dialog.
The method that opens the dialog on my list page (which works just fine):
loadAgencyDialog(id){
this.dialogService.open({ viewModel: AddAgency, model: { id: id }}).then((result) => {
if (!result.wasCancelled) {
console.log('good');
console.log(result.output);
} else {
console.log('bad');
}
});
My modal add-agency.js (VM for the modal, also loads the select list just fine and yes, I have a variable named kase because case is reserved):
import {DialogController} from 'aurelia-dialog';
import {ApiClient} from 'lib/api-client';
import {inject} from 'aurelia-framework';
#inject(DialogController, apiClient)
export class AddAgency {
kase = { id: '' };
constructor(controller, apiClient){
this.controller = controller;
this.agencies = [];
this.apiClient = apiClient;
}
activate(kase){
this.kase = kase;
this.apiClient.get('agencies')
.then(response => response.json())
.then(agencies => this.agencies = agencies.data)
.then(() => console.log(this.agencies)); //these load fine
}
addAgency() {
//Do API call to save the agency here, but how?
}
}
This is part I'm unsure about. In the example, they use controller.ok(theobjectpassedin), which returns a promise. But I don't get where I can call my addAgency method. Any ideas?
It's possible I'm misunderstanding your question, but you should be able to just call addAgency() in your HTML:
<button click.trigger="addAgency()">Add</button>
And then do what you need to do in addAgency(), finishing with a call to this.controller.ok() to wrap up the modal.
As an example, here's my modal's dialog-footer:
<ai-dialog-footer>
<button click.trigger="controller.cancel()">Cancel</button>
<button click.trigger="ok(item)">Save</button>
</ai-dialog-footer>
And in my code:
ok(item) {
this.controller.ok(item);
}
Not too complex. Hope that helps.
I know that InfiniteScroll and Masonry work well together. But I am using the Infinite Scroll Extension of Yii (called yiinfinite-scroll) and tried to apply Masonry on it. Infinite Scroll for itself works perfectly, Masonry for itself too. But after InfiniteScroll tries to load a new set of images (I've got an image page), the callback part of InfiniteScroll doesn't seem to fire, because the newly appended elements don't have any masonry code in it and appear behind the first visible items. (I know that this bug is reported often, but the solutions I found so far didn't work for me).
My structure for showing the picture looks like this:
<div class="items">
<div class="pic">...</pic>
<div class="pic">...</pic>
...
</div>
The first page load looks like this
<div class="items masonry" style="...">
<div class="pic masonry-brick" ...></div>
<div class="pic masonry-brick" ...></div>
...
</div> // everything's fine, masonry is injected into the code
After infinite scroll dynamically loads new images these look like this:
<div class="items masonry" ...></div>
<div class="pic masonry-brick" ...></div>
...
// appended pics:
<div class="pic"></div>
<div class="pic"></div>
</div> // so no masonry functionality was applied
My Masonry Code:
$(function (){
var $container = $('.items');
$container.imagesLoaded(function(){
$container.masonry({
itemSelector: '.pic',
columnWidth: 405
});
});
});
$container.infinitescroll({
// normally, the options are found here. but as I use infinitescroll as a Yii extension, the plugin is already initiated with options
}
},
// trigger Masonry as a callback
function( newElements ) {
// hide new items while they are loading
var $newElems = $( newElements ).css({ opacity: 0 });
// ensure that images load before adding to masonry layout
$newElems.imagesLoaded(function(){
// show elems now they're ready
$newElems.animate({ opacity: 1 });
$container.masonry( 'appended', $newElems, true );
});
});
});
I also tried to copy and replace the current InfiniteScroll-min.js file in the extension folder by the newest one. Same effect...
Best regards,
Sebastian
Okay I found a solution. I post it here if somebody else has the same issue:
I just modified the YiinfiniteScroller Class from the Yiinfinite Scroll Yii Extension and added the callback part for Infinite Scroll which was missing:
private function createInfiniteScrollScript() {
Yii::app()->clientScript->registerScript(
uniqid(),
"$('{$this->contentSelector}').infinitescroll(".$this->buildInifiniteScrollOptions().", ".$this->callback.");"
);
}
At the beginning of the class I added the line
public $callback;
to use it later in the method.
Then you can call the Widget with an additional option callback, for example like this:
'callback' => 'function( newElements ) {
// hide new items while they are loading
var $newElems = $( newElements ).css({ opacity: 0 });
// ensure that images load before adding to masonry layout
$newElems.imagesLoaded(function(){
// show elems now theyre ready
$newElems.animate({ opacity: 1 });
$(".items").masonry( "appended", $newElems, true );
});
}',
Works like charm.