Skip to content

Commit

Permalink
refactor: reduce-select as component
Browse files Browse the repository at this point in the history
  • Loading branch information
arildm committed Oct 2, 2024
1 parent 6456a96 commit 7aebcaa
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 163 deletions.
176 changes: 176 additions & 0 deletions app/scripts/components/reduce-select.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/** @format */
import _ from "lodash"
import angular, { IController, IScope, ITimeoutService } from "angular"
import { html } from "@/util"
import { AttributeOption } from "@/corpus_listing"

type ReduceSelectScope = IScope & {
keyItems: Record<string, Item>
hasWordAttrs: boolean
hasStructAttrs: boolean
toggleSelected: (value: string, event: MouseEvent) => void
toggleWordInsensitive: (event: MouseEvent) => void
toggled: (open: boolean) => void
}

type Item = AttributeOption & {
selected?: boolean
insensitive?: boolean
}

type ReduceSelectController = IController & {
items: Item[]
selected: string[]
insensitive: string[]
onChange: (value: { selected?: string[]; insensitive?: string[] }) => void
}

angular.module("korpApp").component("reduceSelect", {
template: html`<div
uib-dropdown
auto-close="outsideClick"
class="reduce-attr-select"
on-toggle="toggled(open)"
style="width: 200px"
>
<div
uib-dropdown-toggle
class="reduce-dropdown-button inline-block align-middle bg-white border border-gray-500"
>
<div class="reduce-dropdown-button-text">
<span>{{ "reduce_text" | loc:$root.lang }}:</span>
<span> {{keyItems[$ctrl.selected[0]].label | locObj:$root.lang}} </span>
<span ng-if="$ctrl.selected.length > 1"> (+{{ $ctrl.selected.length - 1 }}) </span>
<span class="caret"></span>
</div>
</div>
<div class="reduce-dropdown-menu" uib-dropdown-menu>
<ul>
<li
ng-click="toggleSelected('word', $event)"
ng-class="keyItems['word'].selected ? 'selected':''"
class="attribute"
>
<input type="checkbox" class="reduce-check" ng-checked="keyItems['word'].selected" />
<span class="reduce-label">{{keyItems['word'].label | locObj:$root.lang }}</span>
<span
ng-class="keyItems['word'].insensitive ? 'selected':''"
class="insensitive-toggle"
ng-click="toggleWordInsensitive($event)"
><b>Aa</b></span
>
</li>
<b ng-if="hasWordAttrs">{{'word_attr' | loc:$root.lang}}</b>
<li
ng-repeat="item in $ctrl.items | filter:{ group: 'word_attr' }"
ng-click="toggleSelected(item.value, $event)"
ng-class="item.selected ? 'selected':''"
class="attribute"
>
<input type="checkbox" class="reduce-check" ng-checked="item.selected" />
<span class="reduce-label">{{item.label | locObj:$root.lang }}</span>
</li>
<b ng-if="hasStructAttrs">{{'sentence_attr' | loc:$root.lang}}</b>
<li
ng-repeat="item in $ctrl.items | filter:{ group: 'sentence_attr' }"
ng-click="toggleSelected(item.value, $event)"
ng-class="item.selected ? 'selected':''"
class="attribute"
>
<input type="checkbox" class="reduce-check" ng-checked="item.selected" />
<span class="reduce-label">{{item.label | locObj:$root.lang }}</span>
</li>
</ul>
</div>
</div>`,
bindings: {
items: "<",
selected: "<",
insensitive: "<",
onChange: "<",
},
controller: [
"$scope",
function (scope: ReduceSelectScope) {
const $ctrl = this as ReduceSelectController

$ctrl.$onChanges = (changes) => {
if ("items" in changes && $ctrl.items) {
scope.keyItems = _.keyBy($ctrl.items, "value")
scope.hasWordAttrs = $ctrl.items.some((item) => item.group == "word_attr")
scope.hasStructAttrs = $ctrl.items.some((item) => item.group == "sentence_attr")
}

for (const name of $ctrl.selected || []) {
if (name in scope.keyItems) scope.keyItems[name].selected = true
}

for (const name of $ctrl.insensitive || []) {
if (name in scope.keyItems) scope.keyItems[name].insensitive = true
}

// Only after initialization
if ($ctrl.items && $ctrl.selected && $ctrl.insensitive) validate()
}

/** Report any changes upwards */
function updateSelected() {
validate()

const selected = $ctrl.items.filter((item) => item.selected).map((item) => item.value)
const insensitive = $ctrl.items.filter((item) => item.insensitive).map((item) => item.value)

$ctrl.onChange({
// Only set values that have changed
selected: !_.isEqual(selected, $ctrl.selected) ? selected : undefined,
insensitive: !_.isEqual(insensitive, $ctrl.insensitive) ? insensitive : undefined,
})
}

/** Fix state inconsistencies */
function validate() {
// If no selection given, default to selecting the word option
const hasSelection = $ctrl.items.some((item) => item.selected)
if (!hasSelection) {
scope.keyItems["word"].selected = true
}

// If unselecting the word option, reset the insensitive flag.
if (!scope.keyItems["word"].selected) {
scope.keyItems["word"].insensitive = false
}
}

scope.toggleSelected = function (value, event) {
event.stopPropagation()
const item = scope.keyItems[value]
const isLinux = window.navigator.userAgent.indexOf("Linux") !== -1
if (isLinux ? event.ctrlKey : event.altKey) {
// Unselect all options and select only the given option
$ctrl.items.forEach((item) => (item.selected = false))
item.selected = true
} else {
item.selected = !item.selected
}
updateSelected()
}

scope.toggleWordInsensitive = function (event) {
event.stopPropagation()
scope.keyItems["word"].insensitive = !scope.keyItems["word"].insensitive
if (!scope.keyItems["word"].selected) {
scope.keyItems["word"].selected = true
}
updateSelected()
}

scope.toggled = function (open) {
// if no element is selected when closing popop, select word
if (!open && !$ctrl.selected.length) {
scope.keyItems["word"].selected = true
updateSelected()
}
}
},
],
})
15 changes: 8 additions & 7 deletions app/scripts/components/searchtabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import "@/components/extended/extended-standard"
import "@/components/extended/extended-parallel"
import "@/components/advanced-search"
import "@/components/compare-search"
import "@/components/reduce-select"
import "@/directives/click-cover"
import "@/directives/reduce-select"
import "@/directives/tab-hash"

angular.module("korpApp").component("searchtabs", {
Expand Down Expand Up @@ -63,11 +63,9 @@ angular.module("korpApp").component("searchtabs", {
<span>{{'statistics' | loc:$root.lang}}:</span>
<reduce-select
class="ml-2 relative -top-px"
reduce-items="$ctrl.statCurrentAttrs"
reduce-selected="$ctrl.statSelectedAttrs"
reduce-insensitive="$ctrl.statInsensitiveAttrs"
reduce-lang="lang"
style="width: 200px"
items="$ctrl.statCurrentAttrs"
selected="$ctrl.statSelectedAttrs"
insensitive="$ctrl.statInsensitiveAttrs"
on-change="$ctrl.reduceOnChange"
></reduce-select>
</div>
Expand Down Expand Up @@ -192,7 +190,10 @@ angular.module("korpApp").component("searchtabs", {
}
})

$ctrl.reduceOnChange = () => {
$ctrl.reduceOnChange = ({ selected, insensitive }) => {
if (selected) $ctrl.statSelectedAttrs = selected
if (insensitive) $ctrl.statInsensitiveAttrs = insensitive

if ($ctrl.statSelectedAttrs && $ctrl.statSelectedAttrs.length > 0) {
if ($ctrl.statSelectedAttrs.length != 1 || !$ctrl.statSelectedAttrs.includes("word")) {
$location.search("stats_reduce", $ctrl.statSelectedAttrs.join(","))
Expand Down
155 changes: 0 additions & 155 deletions app/scripts/directives/reduce-select.ts

This file was deleted.

5 changes: 4 additions & 1 deletion app/styles/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1980,6 +1980,9 @@ li.active a span .num-to-find {
background: rgb(242, 247, 255);
}
.attribute {
display: flex;
overflow-x: hidden;
white-space: nowrap;
cursor: pointer;
&:hover {
background-color : lighten(#999, 30%);
Expand All @@ -1991,7 +1994,7 @@ li.active a span .num-to-find {
margin-right: 4px;
}
.reduce-label {
position: absolute;
flex-grow: 1;
}
ul {
margin: 0;
Expand Down

0 comments on commit 7aebcaa

Please sign in to comment.