Skip to content

Commit 9bc59e1

Browse files
committed
feat: enhance UI with icon buttons and improved accessibility
- Introduced icon buttons across various components (ComposeEditor, FilterEditor, TransformEditor, KnownStructures, and more) to improve visual clarity and user interaction. - Updated button elements to include titles and aria-labels for better accessibility and user guidance. - Enhanced styling for buttons to maintain consistency and improve user experience. - Implemented new icons for actions such as download, copy, clear, and inspect to streamline workflows.
1 parent 8c1bf5b commit 9bc59e1

40 files changed

Lines changed: 746 additions & 221 deletions

src/ComposeEditor.vue

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
import store from './store';
33
import {onActivated} from 'vue';
44
import CodeEditor from './components/CodeEditor.vue';
5+
import IconExport from './components/icons/IconExport.vue';
6+
import IconCopy from './components/icons/IconCopy.vue';
7+
import IconRefresh from './components/icons/IconRefresh.vue';
58
import {
69
composeTransformationScript,
710
getGeneratedScriptFilename,
@@ -42,9 +45,15 @@ onActivated(() => recompose());
4245
<div class="composer-controller">
4346
<div class="btn-group">
4447
<span class="composer-btn-group">
45-
<button class="btn btn-download" @click="downloadFlaster()">Download</button>
46-
<button class="btn btn-copy" @click="copy()">Copy</button>
47-
<button class="btn btn-recompose" @click="recompose()">Re-compose</button>
48+
<button class="btn btn-download icon-btn" title="Download the generated script" aria-label="Download script" @click="downloadFlaster()">
49+
<icon-export />
50+
</button>
51+
<button class="btn btn-copy icon-btn" title="Copy the generated script" aria-label="Copy script" @click="copy()">
52+
<icon-copy />
53+
</button>
54+
<button class="btn btn-recompose icon-btn" title="Re-compose the generated script from the current pipeline" aria-label="Re-compose script" @click="recompose()">
55+
<icon-refresh />
56+
</button>
4857
</span>
4958
</div>
5059
<fieldset class="composer-editor-wrapper">

src/FilterEditor.vue

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import CodeEditor from './components/CodeEditor.vue';
55
import IconTrash from './components/icons/IconTrash.vue';
66
import IconCheckboxActive from './components/icons/IconCheckboxActive.vue';
77
import IconCheckboxInactive from './components/icons/IconCheckboxInactive.vue';
8+
import IconPlus from './components/icons/IconPlus.vue';
9+
import IconEye from './components/icons/IconEye.vue';
10+
import IconStructure from './components/icons/IconStructure.vue';
811
912
const initialValue = `// write content for the filter function \`(n) => {<your code>}\`, like:
1013
n.type === 'Literal' &&
@@ -69,15 +72,15 @@ function addNewFilter() {
6972
<div class="filter-controller" v-if="store.arb?.ast?.length">
7073
<div class="btn-group">
7174
<span class="filters-btn-group">
72-
<button class="btn btn-apply" @click="addNewFilter">Add</button>
73-
<button class="btn btn-clear" @click="setFilterEditorContent('')">Clear</button>
75+
<button class="btn btn-apply icon-btn" title="Add the current filter editor code as a filter" aria-label="Add filter" @click="addNewFilter"><icon-plus /></button>
76+
<button class="btn btn-clear icon-btn" title="Clear the filter editor" aria-label="Clear filter editor" @click="setFilterEditorContent('')"><icon-trash /></button>
7477
</span>
7578
<span class="filters-btn-group">
76-
<button class="btn btn-clear-all-filters" @click="clearAllFilters" :disabled="!numOfAvailableFilters">Clear all</button>
77-
<button class="btn btn-clear-all-filters" @click="store.areFiltersActive = !store.areFiltersActive"
78-
:disabled="!numOfAvailableFilters">{{ store.areFiltersActive ? messages.disableFilters : messages.enableFilters }}
79+
<button class="btn btn-clear-all-filters icon-btn" title="Clear all applied filters" aria-label="Clear all filters" @click="clearAllFilters" :disabled="!numOfAvailableFilters"><icon-trash /></button>
80+
<button class="btn btn-clear-all-filters icon-btn" @click="store.areFiltersActive = !store.areFiltersActive"
81+
:disabled="!numOfAvailableFilters" :title="store.areFiltersActive ? messages.disableFilters : messages.enableFilters" :aria-label="store.areFiltersActive ? messages.disableFilters : messages.enableFilters"><icon-eye />
7982
</button>
80-
<button class="btn btn-clear-all-filters" :disabled="!numOfEnabledFilters || numOfEnabledFilters < 2" @click="combineEnabledFilters">Combine active</button>
83+
<button class="btn btn-clear-all-filters icon-btn" :disabled="!numOfEnabledFilters || numOfEnabledFilters < 2" title="Combine all active filters into one filter" aria-label="Combine active filters" @click="combineEnabledFilters"><icon-structure /></button>
8184
</span>
8285
</div>
8386
<div class="filter-display">

src/KnownStructures.vue

Lines changed: 57 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
<script setup>
22
import {computed, reactive} from 'vue';
33
import store from './store';
4+
import IconSearch from './components/icons/IconSearch.vue';
5+
import IconTrash from './components/icons/IconTrash.vue';
6+
import IconEye from './components/icons/IconEye.vue';
7+
import IconCopy from './components/icons/IconCopy.vue';
8+
import IconPreview from './components/icons/IconPreview.vue';
9+
import IconCheck from './components/icons/IconCheck.vue';
10+
import IconReset from './components/icons/IconReset.vue';
11+
import IconArrowLeft from './components/icons/IconArrowLeft.vue';
12+
import IconArrowRight from './components/icons/IconArrowRight.vue';
13+
import IconRefresh from './components/icons/IconRefresh.vue';
14+
import IconListChecks from './components/icons/IconListChecks.vue';
415
516
/**
617
* @typedef {ReturnType<typeof store.getKnownStructureMatches>[number]} KnownStructureMatch
@@ -57,21 +68,7 @@ const filteredStructures = computed(() => {
5768
const visibleStructureIdsForScan = computed(() => filteredStructures.value.map((structure) => structure.id));
5869
const canScanSelected = computed(() => store.hasPendingKnownStructureScan());
5970
const canScanVisible = computed(() => store.canRunKnownStructureMatching(visibleStructureIdsForScan.value));
60-
const scanSelectedTitle = computed(() => {
61-
if (!store.arb?.ast?.length) {
62-
return 'Parse a script before scanning structures';
63-
}
64-
65-
if (!store.canRunKnownStructureMatching()) {
66-
return 'Select at least one runnable structure to scan';
67-
}
68-
69-
if (canScanSelected.value) {
70-
return 'Scan the selected structures against the current parsed script';
71-
}
72-
73-
return 'Selected structure matches are already up to date';
74-
});
71+
const scanSelectedTitle = 'Scan the selected structures against the current parsed script';
7572
7673
const selectedStructureCount = computed(() => store.selectedKnownStructureIds.length);
7774
const activeStructure = computed(() => store.getKnownStructureById(store.activeKnownStructureId));
@@ -245,6 +242,10 @@ function applyTransform(structureId) {
245242
store.applyKnownStructureTransform(structureId);
246243
}
247244
245+
function hasStructureMatches(structureId) {
246+
return store.getKnownStructureMatches(structureId).length > 0;
247+
}
248+
248249
/**
249250
* Selects a normalized structure match in the store.
250251
*
@@ -412,23 +413,25 @@ function createDeterministicRandom(match, seed) {
412413
</div>
413414
<div class="toolbar-group">
414415
<button
415-
class="btn btn-run"
416+
class="btn btn-run icon-btn"
416417
:disabled="!canScanSelected"
417418
:title="scanSelectedTitle"
419+
aria-label="Scan selected structures"
418420
@click="runStructures()"
419421
>
420-
Scan selected
422+
<icon-search />
421423
</button>
422424
<button
423-
class="btn"
425+
class="btn icon-btn"
424426
:disabled="!canScanVisible"
425427
title="Scan the currently visible structures against the current parsed script"
428+
aria-label="Scan visible structures"
426429
@click="runStructures(visibleStructureIdsForScan)"
427430
>
428-
Scan visible
431+
<icon-eye />
429432
</button>
430-
<button class="btn" @click="store.clearKnownStructureResults()">
431-
Clear all
433+
<button class="btn icon-btn" title="Clear all known structure results" aria-label="Clear all structure results" @click="store.clearKnownStructureResults()">
434+
<icon-trash />
432435
</button>
433436
<span class="toolbar-meta">{{ selectedStructureCount }} selected</span>
434437
<span class="toolbar-meta" v-if="store.knownStructureExecutionStatus.blockedStructures">
@@ -479,18 +482,21 @@ function createDeterministicRandom(match, seed) {
479482
</span>
480483
</div>
481484
<div class="structure-actions" @click.stop>
482-
<button class="btn btn-inline btn-run" :disabled="!structure.browserRunnable" @click="runStructures([structure.id])">Scan</button>
483-
<button class="btn btn-inline" @click="showStructure(structure.id)">Show</button>
484-
<button class="btn btn-inline" @click="store.clearKnownStructureMatches(structure.id)">Clear</button>
485-
<button class="btn btn-inline" @click="copyRuleSeed(structure.id)">Copy seed</button>
485+
<button class="btn btn-inline btn-run icon-btn icon-btn-sm" :disabled="!structure.browserRunnable" title="Scan this structure" aria-label="Scan structure" @click="runStructures([structure.id])"><icon-search /></button>
486+
<button class="btn btn-inline icon-btn icon-btn-sm" :disabled="!hasStructureMatches(structure.id)" title="Show matches for this structure" aria-label="Show structure matches" @click="showStructure(structure.id)"><icon-list-checks /></button>
487+
<button class="btn btn-inline icon-btn icon-btn-sm" title="Clear this structure's matches" aria-label="Clear structure matches" @click="store.clearKnownStructureMatches(structure.id)"><icon-trash /></button>
488+
<button class="btn btn-inline icon-btn icon-btn-sm" title="Copy this structure's rule seed" aria-label="Copy rule seed" @click="copyRuleSeed(structure.id)"><icon-copy /></button>
486489
<button
487490
v-if="structure.transformEnabled"
488-
class="btn btn-inline"
491+
class="btn btn-inline icon-btn icon-btn-sm preview-icon-btn"
492+
:disabled="!hasStructureMatches(structure.id)"
493+
title="Preview this structure's transform"
494+
aria-label="Preview transform"
489495
@click="previewTransform(structure.id)"
490496
>
491-
Preview
497+
<icon-preview />
492498
</button>
493-
<button class="btn btn-inline" @click="store.setInspectedKnownStructure(structure.id)">Inspect</button>
499+
<button class="btn btn-inline icon-btn icon-btn-sm" title="Inspect this structure" aria-label="Inspect structure" @click="store.setInspectedKnownStructure(structure.id)"><icon-eye /></button>
494500
</div>
495501
</article>
496502
</div>
@@ -502,21 +508,11 @@ function createDeterministicRandom(match, seed) {
502508
<span v-if="activeMatches.length"> {{ activeMatchPosition.current }} / {{ activeMatchPosition.total }}</span>
503509
</legend>
504510
<div class="results-toolbar">
505-
<button class="btn btn-inline" @click="store.selectKnownStructureMatchStep(-1)" :disabled="!activeMatches.length">
506-
Prev match
507-
</button>
508-
<button class="btn btn-inline" @click="store.selectKnownStructureMatchStep(1)" :disabled="!activeMatches.length">
509-
Next match
510-
</button>
511-
<button class="btn btn-inline" @click="store.selectKnownStructureStep(-1)" :disabled="!availableStructureFilterOptions.length">
512-
Prev structure
513-
</button>
514-
<button class="btn btn-inline" @click="store.selectKnownStructureStep(1)" :disabled="!availableStructureFilterOptions.length">
515-
Next structure
516-
</button>
517-
<button class="btn btn-inline" @click="store.rerunKnownStructureMatching()" :disabled="!store.lastKnownStructureRunIds.length">
518-
Scan last run
519-
</button>
511+
<button class="btn btn-inline icon-btn icon-btn-sm" title="Previous match" aria-label="Previous match" @click="store.selectKnownStructureMatchStep(-1)" :disabled="!activeMatches.length"><icon-arrow-left /></button>
512+
<button class="btn btn-inline icon-btn icon-btn-sm" title="Next match" aria-label="Next match" @click="store.selectKnownStructureMatchStep(1)" :disabled="!activeMatches.length"><icon-arrow-right /></button>
513+
<button class="btn btn-inline icon-btn icon-btn-sm" title="Previous structure" aria-label="Previous structure" @click="store.selectKnownStructureStep(-1)" :disabled="!availableStructureFilterOptions.length"><icon-arrow-left /></button>
514+
<button class="btn btn-inline icon-btn icon-btn-sm" title="Next structure" aria-label="Next structure" @click="store.selectKnownStructureStep(1)" :disabled="!availableStructureFilterOptions.length"><icon-arrow-right /></button>
515+
<button class="btn btn-inline icon-btn icon-btn-sm" title="Scan the last run set again" aria-label="Scan last run again" @click="store.rerunKnownStructureMatching()" :disabled="!store.lastKnownStructureRunIds.length"><icon-refresh /></button>
520516
<label class="toggle-inline">
521517
<input
522518
:checked="store.scrollKnownStructureSelectionIntoView"
@@ -562,10 +558,12 @@ function createDeterministicRandom(match, seed) {
562558
>
563559
<button
564560
v-if="exploration.samplingMode === 'random'"
565-
class="btn btn-inline"
561+
class="btn btn-inline icon-btn icon-btn-sm"
562+
title="Reroll the random sample"
563+
aria-label="Reroll sample"
566564
@click="rerollSample()"
567565
>
568-
Reroll sample
566+
<icon-refresh />
569567
</button>
570568
<span class="toolbar-meta">{{ sampledResults.length }} visible matches</span>
571569
</div>
@@ -667,7 +665,7 @@ function createDeterministicRandom(match, seed) {
667665
<strong>Intent</strong> {{ inspectedStructure.description }}
668666
</div>
669667
<pre class="rule-seed">{{ activeRuleSeed }}</pre>
670-
<button class="btn btn-inline" @click="copyRuleSeed(inspectedStructure.id)">Copy seed</button>
668+
<button class="btn btn-inline icon-btn icon-btn-sm" title="Copy this structure's rule seed" aria-label="Copy rule seed" @click="copyRuleSeed(inspectedStructure.id)"><icon-copy /></button>
671669
</div>
672670
<div class="detail-card">
673671
<div class="detail-heading">Safe transform</div>
@@ -700,25 +698,31 @@ function createDeterministicRandom(match, seed) {
700698
</div>
701699
<div class="structure-actions">
702700
<button
703-
class="btn btn-inline"
704-
:disabled="!inspectedStructure.transformEnabled"
701+
class="btn btn-inline icon-btn icon-btn-sm preview-icon-btn"
702+
:disabled="!inspectedStructure.transformEnabled || !hasStructureMatches(inspectedStructure.id)"
703+
title="Preview this structure's transform"
704+
aria-label="Preview transform"
705705
@click="previewTransform(inspectedStructure.id)"
706706
>
707-
Preview transform
707+
<icon-preview />
708708
</button>
709709
<button
710-
class="btn btn-inline btn-run"
710+
class="btn btn-inline btn-run icon-btn icon-btn-sm"
711711
:disabled="!inspectedStructure.transformEnabled || !activeTransformPreview || !!activeTransformPreview.error || !activeTransformPreview.hasChanges"
712+
title="Apply the previewed transform"
713+
aria-label="Apply transform"
712714
@click="applyTransform(inspectedStructure.id)"
713715
>
714-
Apply transform
716+
<icon-check />
715717
</button>
716718
<button
717-
class="btn btn-inline"
719+
class="btn btn-inline icon-btn icon-btn-sm"
718720
:disabled="!activeTransformPreview"
721+
title="Clear the current transform preview"
722+
aria-label="Clear preview"
719723
@click="store.clearKnownStructureTransformPreview(inspectedStructure.id)"
720724
>
721-
Clear preview
725+
<icon-reset />
722726
</button>
723727
</div>
724728
</div>

src/TransformEditor.vue

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
<script setup>
22
import store from './store';
33
import CodeEditor from './components/CodeEditor.vue';
4+
import IconCheck from './components/icons/IconCheck.vue';
5+
import IconReset from './components/icons/IconReset.vue';
6+
import IconTrash from './components/icons/IconTrash.vue';
47
58
const initialValue = `// write the logic to apply to the node \`(n) => {<your code>}\`.
69
// to delete a node use arb.markNode(n);
@@ -37,9 +40,9 @@ function revertTransformation() {
3740
<div class="transformer-controller" v-if="store.arb?.ast?.length">
3841
<div class="btn-group">
3942
<span class="transform-edit-btn-group">
40-
<button class="btn btn-apply" @click="applyTransformation()">Apply</button>
41-
<button class="btn btn-revert" @click="revertTransformation" :disabled="!store.states.length">Revert</button>
42-
<button class="btn btn-clear" @click="setTransformEditorContent('')">Clear</button>
43+
<button class="btn btn-apply icon-btn" title="Apply the current transform editor code" aria-label="Apply transform" @click="applyTransformation()"><icon-check /></button>
44+
<button class="btn btn-revert icon-btn" title="Revert the last transformation state" aria-label="Revert transformation" @click="revertTransformation" :disabled="!store.states.length"><icon-reset /></button>
45+
<button class="btn btn-clear icon-btn" title="Clear the transform editor" aria-label="Clear transform editor" @click="setTransformEditorContent('')"><icon-trash /></button>
4346
</span>
4447
</div>
4548
<div class="transformer-display">

src/assets/main.css

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,84 @@ body {
1919
align-items: center;
2020
justify-content: center;
2121
gap: 0.4rem;
22+
transition:
23+
background-color 140ms ease,
24+
border-color 140ms ease,
25+
color 140ms ease,
26+
box-shadow 140ms ease,
27+
transform 140ms ease,
28+
opacity 140ms ease;
29+
}
30+
31+
.icon-btn {
32+
width: 2.3rem;
33+
min-width: 2.3rem;
34+
min-height: 2.3rem;
35+
padding: 0.2rem;
36+
line-height: 1;
37+
flex: 0 0 auto;
38+
}
39+
40+
.icon-btn svg {
41+
width: 1.2rem;
42+
height: 1.2rem;
43+
display: block;
44+
margin: auto;
45+
flex: 0 0 auto;
46+
}
47+
48+
.icon-btn-sm {
49+
width: 2.1rem;
50+
min-width: 2.1rem;
51+
min-height: 2.1rem;
52+
padding: 0.14rem;
53+
}
54+
55+
.icon-btn-sm svg {
56+
width: 1.25rem;
57+
height: 1.25rem;
58+
}
59+
60+
.preview-icon-btn {
61+
border-color: transparent;
62+
background: transparent;
63+
}
64+
65+
.preview-icon-btn svg {
66+
width: 1.45rem;
67+
height: 1.45rem;
68+
}
69+
70+
.preview-icon-btn:hover:not(:disabled),
71+
.preview-icon-btn:focus-visible:not(:disabled) {
72+
background: rgba(255, 191, 102, 0.14);
73+
border-color: rgba(255, 191, 102, 0.28);
74+
box-shadow: inset 0 0 0 1px rgba(255, 191, 102, 0.08);
75+
outline: none;
76+
}
77+
78+
.icon-btn:hover:not(:disabled),
79+
.icon-btn:focus-visible:not(:disabled) {
80+
background: rgba(255, 255, 255, 0.1);
81+
border-color: rgba(255, 255, 255, 0.2);
82+
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.05);
83+
outline: none;
84+
}
85+
86+
.icon-btn:active:not(:disabled) {
87+
transform: translateY(1px);
88+
}
89+
90+
.sr-only {
91+
position: absolute;
92+
width: 1px;
93+
height: 1px;
94+
padding: 0;
95+
margin: -1px;
96+
overflow: hidden;
97+
clip: rect(0, 0, 0, 0);
98+
white-space: nowrap;
99+
border: 0;
22100
}
23101

24102
.btn:active {

0 commit comments

Comments
 (0)