The toggle works only if clicking on a button, ignores the div, seems button triggers some sort of state change, how do I get this to work when clicking anywhere ?
ToolBar.ts
export default class ToolBar {
options:Array<ToolBarOptions>;
constructor() {
this.options = [
new ToolBarOptions(ToolButton.sideMenu,SideMenuIcon,false,true, []),
new ToolBarOptions(ToolButton.mainMenu,MainMenuIcon,false,true, [ new ToolBarOptions(ToolButton.export,exportIcon,true,true,[])]),
new ToolBarOptions(ToolButton.entities,EntityIcon,false,true,[]),
new ToolBarOptions(ToolButton.setting,settingsIcon,false,true,[]),
];
}
}
class ToolBarOptions {
disabled:boolean;
name:ToolButton;
icon:string;
show:boolean;
options:Array<ToolBarOptions>;
constructor(name: ToolButton,icon:string,disabled:boolean,show:boolean, options:Array<ToolBarOptions>) {
this.name = name;
this.disabled = disabled;
this.icon = icon;
this.show=show;
this.options=options;
}
}
export const enum ToolButton{
mainMenu="mainMenu",
export="export",
entities="entities",
sideMenu="sideMenu",
setting="setting",
}
App.svelte
let toolbarOptions = new ToolBar();
function handleClickOutSide() {
console.log(toolbarOptions.options)
toolbarOptions.options.forEach((o) => {
o.show=!o.show;
});
console.log(toolbarOptions.options)
<div on:click={handleClickOutSide } >
<ul >
{#each toolbarOptions.options as {name, icon,options, show }, i}
<li>
<button on:click={()=>show=!show} name={name} h-10":""}">
{#if toolbarOptions.options.length-1 ===i}
<div>100%</div>
{/if}
<icon> {@html icon}</icon>
<span>
<svg fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" >
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" />
</svg>
</span>
{#if options.length >0 }
<div hidden":""}">
<ul >
{#each options as {name, icon,show }}
<li >
<span > {@html icon} </span>
<span > {name}</span>
</li>
{/each}
</ul>
</div>
{/if}
</button>
</li>
{/each}
</ul>
</div>
CodePudding user response:
When interacting with each item in a list there are several options that take reactivity into account:
- Use item index access
- Map all items
- Use a dummy assignment
Examples for each:
let items = [
{ name: 'Item 1', checked: false },
{ name: 'Item 2', checked: false },
{ name: 'Item 3', checked: false },
];
const toggleViaIndex = () =>
items.forEach((e, i) => items[i].checked = !items[i].checked);
const toggleViaMap = () =>
items = items.map(item => ({ ...item, checked: !item.checked }));
const toggleViaDummyAssignment = () => {
items.forEach(item => item.checked = !item.checked);
items = items;
}
Personally, I am not a fan of the dummy assignment, because it looks like a useless statement. You can of course add comments to make it clearer why the statement exists.
I would not recommend using classes unless necessary, by the way. It breaks things like the map approach, if the class defines any functions or the prototype matters, because those aspects get lost in the spread.
Also: click
events on things that are not button
elements should always have a keyboard equivalent for accessibility; in this case probably an escape press.
You also probably should just set show
to false
rather than invert it on click outside.
By the way, ToolBarOptions
could be shortened significantly via TypeScript:
class ToolBarOptions {
constructor(
public name: ToolButton,
public icon: string,
public disabled: boolean,
public show: boolean,
public options: Array<ToolBarOptions>,
) { }
}