Files
privacy.sexy/src/presentation/components/Scripts/View/TheScriptsView.vue
undergroundwires ae172000a6 Centralize and use global spacing variables
This commit improves UI consistency. It also improves maintainability by
removing "magic values" in favor of standardized spacing throughout the
application.

- Adjust spacing variables to match the convention.
- Add `_spacing.scss` to define a centralized set of spacing variables, both
  absolute and relative, to standardize the spacing throughout the application.
  This new approach ensures a consistent spacing logic across all components and
  layouts, facilitating easier maintenance and scalability of the styling codebase.
- Update various SCSS styles to utilize the new spacing variables. This change
  harmonizes the spacing across different parts of the application, aligning with
  the new design system's principles.
- Slightly adjust existing padding/margin/gaps for better consistency.

Other supporting changes per component:

- RatingCircle: Update style names to match convention and simplify
  hacky way to inject circle width value through CSS variables. Add
  tests for the new behavior and refactor existing tests for easier
  extensibility.
- TheFooter: Add small gap when footer items wrap.
- HiearchicalTreeNode: Refactor variables to separate caret size clearly
  from padding applied.
- App: Make padding responsive as initial behavior of v0.13.0 before
  5d940b57ef.
- ModalDialog: Use responsive absolute values instead of percentage.
- HorizontalResizeSlider:
  - Use `v-bind` instead of hacky way to inject SCSS values through variables.
  - Remove `verticalMargin` property to simplify its styling.
- Move `src/presentation/assets/styles/components/_card.scss` closer to
  components that it styles. Update structure documentation.

The centralization of spacing definitions will aid in future design
adjustments, ensuring that updates to spacing can be made swiftly and
uniformly across the application. It's a step towards a more maintainable
and scalable frontend architecture.
2024-04-12 18:38:12 +02:00

171 lines
4.9 KiB
Vue

<template>
<div class="scripts-view">
<template v-if="!isSearching">
<template v-if="currentView === ViewType.Cards">
<CardList />
</template>
<template v-else-if="currentView === ViewType.Tree">
<ScriptsTree />
</template>
</template>
<template v-else>
<!-- Searching -->
<div class="search">
<div class="search__query">
<div>Searching for "{{ trimmedSearchQuery }}"</div>
<div
class="search__query__close-button"
@click="clearSearchQuery()"
>
<FlatButton icon="xmark" />
</div>
</div>
<div v-if="!searchHasMatches" class="search-no-matches">
<div>Sorry, no matches for "{{ trimmedSearchQuery }}" 😞</div>
<div>
Feel free to extend the scripts
<a :href="repositoryUrl" class="child github" target="_blank" rel="noopener noreferrer">here</a>
</div>
</div>
</div>
<div v-if="searchHasMatches">
<ScriptsTree :has-top-padding="false" />
</div>
</template>
</div>
</template>
<script lang="ts">
import {
defineComponent, type PropType, ref, computed,
} from 'vue';
import { injectKey } from '@/presentation/injectionSymbols';
import ScriptsTree from '@/presentation/components/Scripts/View/Tree/ScriptsTree.vue';
import CardList from '@/presentation/components/Scripts/View/Cards/CardList.vue';
import { ViewType } from '@/presentation/components/Scripts/Menu/View/ViewType';
import type { ReadonlyFilterContext } from '@/application/Context/State/Filter/FilterContext';
import type { FilterResult } from '@/application/Context/State/Filter/Result/FilterResult';
import FlatButton from '@/presentation/components/Shared/FlatButton.vue';
export default defineComponent({
components: {
ScriptsTree,
CardList,
FlatButton,
},
props: {
currentView: {
type: Number as PropType<ViewType>,
required: true,
},
},
setup() {
const { modifyCurrentState, onStateChange } = injectKey((keys) => keys.useCollectionState);
const { events } = injectKey((keys) => keys.useAutoUnsubscribedEvents);
const { projectDetails } = injectKey((keys) => keys.useApplication);
const repositoryUrl = computed<string>(() => projectDetails.repositoryWebUrl);
const searchQuery = ref<string | undefined>();
const isSearching = computed(() => Boolean(searchQuery.value));
const searchHasMatches = ref(false);
const trimmedSearchQuery = computed(() => {
const query = searchQuery.value;
if (!query) {
return '';
}
const threshold = 30;
if (query.length <= threshold - 3) {
return query;
}
return `${query.substring(0, threshold)}...`;
});
onStateChange((newState) => {
updateFromInitialFilter(newState.filter.currentFilter);
events.unsubscribeAllAndRegister([
subscribeToFilterChanges(newState.filter),
]);
}, { immediate: true });
function clearSearchQuery() {
modifyCurrentState((state) => {
const { filter } = state;
filter.clearFilter();
});
}
function updateFromInitialFilter(filter?: FilterResult) {
searchQuery.value = filter?.query;
searchHasMatches.value = filter?.hasAnyMatches() ?? false;
}
function subscribeToFilterChanges(filter: ReadonlyFilterContext) {
return filter.filterChanged.on((event) => {
event.visit({
onApply: (newFilter) => {
searchQuery.value = newFilter.query;
searchHasMatches.value = newFilter.hasAnyMatches();
},
onClear: () => {
searchQuery.value = undefined;
},
});
});
}
return {
repositoryUrl,
trimmedSearchQuery,
isSearching,
searchHasMatches,
clearSearchQuery,
ViewType,
};
},
});
</script>
<style scoped lang="scss">
@use "@/presentation/assets/styles/main" as *;
.scripts-view {
@media screen and (min-width: $media-vertical-view-breakpoint) {
// so the current code is always visible
overflow: auto;
max-height: 70vh;
}
}
.search {
display: flex;
flex-direction: column;
background-color: $color-scripts-bg;
padding-top: $spacing-absolute-large;
padding-bottom:$spacing-absolute-large;
.search__query {
display: flex;
justify-content: center;
flex-direction: row;
align-items: center;
color: $color-primary-light;
.search__query__close-button {
font-size: $font-size-absolute-large;
margin-left: $spacing-relative-x-small;
}
}
.search-no-matches {
display:flex;
flex-direction: column;
word-break:break-word;
color: $color-on-primary;
font-size: $font-size-absolute-large;
padding: $spacing-absolute-medium;
text-align:center;
gap: $spacing-relative-small;
a {
color: $color-primary;
}
}
}
</style>