Skip to content

Commit 53a3501

Browse files
committed
Add Indeterminate state to repositories (#48)
* Add Indeterminate state to repositories * Always return boolean (isIndeterminate)
1 parent 8d5f6ad commit 53a3501

File tree

3 files changed

+63
-5
lines changed

3 files changed

+63
-5
lines changed

src/components/NotificationItem.vue

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ interface Props {
1010
checked?: boolean
1111
checkable?: boolean
1212
checkboxVisible?: boolean
13+
indeterminate?: boolean
1314
}
1415
1516
interface Emits {
@@ -42,10 +43,15 @@ function handleThreadClick(thread: Thread, event: MouseEvent | KeyboardEvent) {
4243
}
4344
4445
function handleRepoClick(repo: MinimalRepository, event: MouseEvent | KeyboardEvent) {
45-
if (!props.checkable || (event instanceof KeyboardEvent && event.repeat))
46+
if ((event instanceof KeyboardEvent && event.repeat))
4647
return
4748
48-
if ((event.ctrlKey || event.metaKey) || isInteractedCheckbox(event)) {
49+
if (props.checkable && ((event.ctrlKey || event.metaKey) || isInteractedCheckbox(event))) {
50+
if (props.indeterminate) {
51+
emit('update:checked', true)
52+
return
53+
}
54+
4955
emit('update:checked', !props.checked)
5056
return
5157
}
@@ -74,11 +80,15 @@ function handleRepoClick(repo: MinimalRepository, event: MouseEvent | KeyboardEv
7480
</span>
7581

7682
<div
77-
v-if="checkable"
83+
v-if="checkable || indeterminate"
7884
class="notification-checkbox-wrapper"
7985
>
8086
<span
81-
:class="{ 'notification-checkbox-checked': checked, 'notification-checkbox-visible': checkboxVisible }"
87+
:class="{
88+
'notification-checkbox-checked': checked,
89+
'notification-checkbox-visible': checkboxVisible,
90+
'notification-checkbox-indeterminate': indeterminate,
91+
}"
8292
role="checkbox"
8393
tabindex="0"
8494
aria-checked="true"
@@ -254,6 +264,7 @@ function handleRepoClick(repo: MinimalRepository, event: MouseEvent | KeyboardEv
254264
flex-shrink: 0;
255265
padding: 3px;
256266
display: inline-flex;
267+
align-items: center;
257268
opacity: 0;
258269
259270
&[data-focus-visible-added] {
@@ -264,18 +275,29 @@ function handleRepoClick(repo: MinimalRepository, event: MouseEvent | KeyboardEv
264275
opacity: 1;
265276
}
266277
267-
&-checked {
278+
&-checked, &-indeterminate {
268279
border-color: var(--accent-color);
269280
270281
.notification-checkbox-dot {
271282
background-color: var(--accent-color);
272283
}
273284
}
274285
286+
&-indeterminate {
287+
.notification-checkbox-dot {
288+
height: 2px;
289+
}
290+
}
291+
275292
&-dot {
276293
width: 100%;
277294
height: 100%;
278295
border-radius: 3px;
296+
297+
&-indeterminate {
298+
background-color: var(--accent-color);
299+
opacity: 1;
300+
}
279301
}
280302
281303
&-wrapper {

src/pages/HomePage.vue

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import Popover from '../components/Popover.vue'
1919
import MenuItems, { menuItem } from '../components/MenuItems.vue'
2020
import { type UseKeyOptions, useKey } from '../composables/useKey'
2121
import { notificationApiMutex } from '../constants'
22+
import { everySome } from '../utils/array'
2223
2324
const store = useStore()
2425
@@ -99,6 +100,22 @@ function isCheckable(item: MinimalRepository | Thread) {
99100
.filter(thread => thread.repository.id === item.id)
100101
.some(thread => thread.unread)
101102
}
103+
104+
function isIndeterminate(item: MinimalRepository | Thread): boolean {
105+
if (isThread(item))
106+
return false
107+
108+
const repoThreads = store.notifications
109+
.filter(isThread)
110+
.filter(thread => thread.unread && thread.repository.id === item.id)
111+
112+
const { every, some } = everySome(repoThreads, thread => (
113+
store.checkedItems.some(checkedItem => checkedItem.id === thread.id)
114+
))
115+
116+
return some && !every
117+
}
118+
102119
onScopeDispose(() => {
103120
store.checkedItems = []
104121
})
@@ -338,6 +355,7 @@ whenever(() => store.skeletonVisible, () => {
338355
:value="item"
339356
:checked="isChecked(item)"
340357
:checkable="isCheckable(item)"
358+
:indeterminate="isIndeterminate(item)"
341359
:checkboxVisible="store.checkedItems.length > 0"
342360
@contextmenu="handleThreadContextmenu"
343361
@click:notification="handleNotificationClick"

src/utils/array.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export type EverySomeCallback<T extends any[]> = (el: T[number], index: number, array: T[]) => boolean
2+
3+
export function everySome<T extends any[]>(array: T, callback: EverySomeCallback<T>) {
4+
let every = true
5+
let some = false
6+
7+
for (let i = 0; i < array.length; i += 1) {
8+
const result = callback(array[i], i, array)
9+
10+
if (every && !result)
11+
every = false
12+
13+
if (!some && result)
14+
some = true
15+
}
16+
17+
return { every, some }
18+
}

0 commit comments

Comments
 (0)