Skip to content

Commit

Permalink
refactor: resource conflict modal
Browse files Browse the repository at this point in the history
Refactors the resource conflict modal to use a custom component. This allows us to remove the secondary action as well as the checkbox form the global modal as they were overly specific. The modal itself should be as slick as possible, anything custom like in this case should be handled by a custom component.
  • Loading branch information
JammingBen committed Dec 19, 2023
1 parent 0614cef commit 923e5c5
Show file tree
Hide file tree
Showing 13 changed files with 247 additions and 177 deletions.
83 changes: 3 additions & 80 deletions packages/design-system/src/components/OcModal/OcModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,6 @@
@keydown.enter.prevent="confirm"
/>
</template>
<div v-if="checkboxLabel" class="oc-modal-body-actions oc-flex oc-flex-left">
<oc-checkbox
v-model="checkboxValue"
size="medium"
:label="checkboxLabel"
:aria-label="checkboxLabel"
/>
</div>
</div>

<div v-if="!hideActions" class="oc-modal-body-actions oc-flex oc-flex-right">
Expand All @@ -70,15 +62,6 @@
@click="cancelModalAction"
v-text="buttonCancelText"
/>
<oc-button
v-if="buttonSecondaryText"
ref="secondaryButton"
class="oc-modal-body-actions-secondary oc-ml-s"
:variation="buttonSecondaryVariation"
:appearance="buttonSecondaryAppearance"
@click="secondaryModalAction"
v-text="buttonSecondaryText"
/>
<oc-button
v-if="!withoutButtonConfirm"
ref="primaryButton"
Expand All @@ -98,7 +81,6 @@
<script lang="ts">
import { defineComponent, PropType, ComponentPublicInstance, ref, onMounted, unref } from 'vue'
import OcButton from '../OcButton/OcButton.vue'
import OcCheckbox from '../OcCheckbox/OcCheckbox.vue'
import OcIcon from '../OcIcon/OcIcon.vue'
import OcTextInput from '../OcTextInput/OcTextInput.vue'
import { FocusTrap } from 'focus-trap-vue'
Expand Down Expand Up @@ -126,7 +108,6 @@ export default defineComponent({
components: {
OcButton,
OcCheckbox,
OcIcon,
OcTextInput,
FocusTrap
Expand Down Expand Up @@ -169,14 +150,6 @@ export default defineComponent({
required: false,
default: null
},
/**
* Modal checkbox label
*/
checkboxLabel: {
type: String,
required: false,
default: ''
},
/**
* Contextual helper label
*/
Expand Down Expand Up @@ -223,36 +196,6 @@ export default defineComponent({
return ['outline', 'filled', 'raw'].includes(value)
}
},
/**
* Text of the secondary button
*/
buttonSecondaryText: {
type: String,
required: false,
default: ''
},
/**
* Variation type of the secondary button
*/
buttonSecondaryVariation: {
type: String,
required: false,
default: 'passive',
validator: (value: string) => {
return ['passive', 'primary', 'danger', 'success', 'warning'].includes(value)
}
},
/**
* Appearance of the secondary button
*/
buttonSecondaryAppearance: {
type: String,
required: false,
default: 'outline',
validator: (value: string) => {
return ['outline', 'filled', 'raw'].includes(value)
}
},
/**
* Text of the confirm button
*/
Expand Down Expand Up @@ -376,28 +319,22 @@ export default defineComponent({
default: false
}
},
emits: ['cancel', 'confirm', 'confirm-secondary', 'input', 'checkbox-changed'],
emits: ['cancel', 'confirm', 'input'],
setup() {
const primaryButton = ref(null)
const secondaryButton = ref(null)
const cancelButton = ref(null)
const setButtonsEqualWidth = () => {
const _primaryButton = unref(primaryButton)
const _secondaryButton = unref(secondaryButton)
const _cancelButton = unref(cancelButton)
const primaryWidth = _primaryButton?.$el?.offsetWidth || 0
const secondaryWidth = _secondaryButton?.$el?.offsetWidth || 0
const cancelWidth = _cancelButton?.$el?.offsetWidth || 0
const maxWidth = Math.max(primaryWidth, secondaryWidth, cancelWidth)
const maxWidth = Math.max(primaryWidth, cancelWidth)
if (_primaryButton?.$el) {
_primaryButton.$el.style.minWidth = `${maxWidth}px`
}
if (_secondaryButton?.$el) {
_secondaryButton.$el.style.minWidth = `${maxWidth}px`
}
if (_cancelButton?.$el) {
_cancelButton.$el.style.minWidth = `${maxWidth}px`
}
Expand All @@ -408,14 +345,12 @@ export default defineComponent({
return {
primaryButton,
secondaryButton,
cancelButton
}
},
data() {
return {
userInputValue: null,
checkboxValue: false
userInputValue: null
}
},
computed: {
Expand Down Expand Up @@ -456,10 +391,6 @@ export default defineComponent({
inputValue: {
handler: 'inputAssignPropAsValue',
immediate: true
},
checkboxValue: {
handler: 'checkboxValueChanged',
immediate: true
}
},
methods: {
Expand All @@ -469,9 +400,6 @@ export default defineComponent({
*/
this.$emit('cancel')
},
secondaryModalAction() {
this.$emit('confirm-secondary')
},
confirm() {
if (this.buttonConfirmDisabled || this.inputError) {
return
Expand All @@ -493,9 +421,6 @@ export default defineComponent({
},
inputAssignPropAsValue(value) {
this.userInputValue = value
},
checkboxValueChanged(value) {
this.$emit('checkbox-changed', value)
}
}
})
Expand Down Expand Up @@ -694,7 +619,6 @@ export default defineComponent({
message="Do you accept our terms of use?"
button-cancel-text="Decline"
button-confirm-text="Accept"
checkbox-label="I accept the terms of use"
class="oc-mb-l oc-position-relative"
/>
</div>
Expand All @@ -708,7 +632,6 @@ export default defineComponent({
message="Do you accept our terms of use?"
button-cancel-text="Decline"
button-confirm-text="Accept"
button-secondary-text="Accept some"
class="oc-mb-l oc-position-relative"
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,9 @@ exports[`OcModal displays input 1`] = `
<!--v-if-->
<!--v-if-->
<oc-text-input-stub class="oc-modal-body-input" clearbuttonaccessiblelabel="" clearbuttonenabled="false" disabled="false" fixmessageline="true" id="oc-textinput-1" label="Folder name" modelvalue="New folder" passwordpolicy="[object Object]" readonly="false" type="text"></oc-text-input-stub>
<!--v-if-->
</div>
<div class="oc-modal-body-actions oc-flex oc-flex-right">
<oc-button-stub appearance="outline" class="oc-modal-body-actions-cancel" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" style="min-width: 0px;" submit="button" type="button" variation="passive">Cancel</oc-button-stub>
<!--v-if-->
<oc-button-stub appearance="filled" class="oc-modal-body-actions-confirm oc-ml-s" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" style="min-width: 0px;" submit="button" type="button" variation="primary">Confirm</oc-button-stub>
</div>
</div>
Expand All @@ -36,11 +34,9 @@ exports[`OcModal hides icon if not specified 1`] = `
<p class="oc-modal-body-message oc-mt-rm oc-mb-rm">Example message</p>
<!--v-if-->
<!--v-if-->
<!--v-if-->
</div>
<div class="oc-modal-body-actions oc-flex oc-flex-right">
<oc-button-stub appearance="outline" class="oc-modal-body-actions-cancel" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" style="min-width: 0px;" submit="button" type="button" variation="passive">Cancel</oc-button-stub>
<!--v-if-->
<oc-button-stub appearance="filled" class="oc-modal-body-actions-confirm oc-ml-s" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" style="min-width: 0px;" submit="button" type="button" variation="primary">Confirm</oc-button-stub>
</div>
</div>
Expand All @@ -60,11 +56,9 @@ exports[`OcModal matches snapshot 1`] = `
<p class="oc-modal-body-message oc-mt-rm oc-mb-rm">Example message</p>
<!--v-if-->
<!--v-if-->
<!--v-if-->
</div>
<div class="oc-modal-body-actions oc-flex oc-flex-right">
<oc-button-stub appearance="outline" class="oc-modal-body-actions-cancel" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" style="min-width: 0px;" submit="button" type="button" variation="passive">Cancel</oc-button-stub>
<!--v-if-->
<oc-button-stub appearance="filled" class="oc-modal-body-actions-confirm oc-ml-s" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" style="min-width: 0px;" submit="button" type="button" variation="primary">Confirm</oc-button-stub>
</div>
</div>
Expand All @@ -84,11 +78,9 @@ exports[`OcModal overrides props message with slot 1`] = `
<div class="oc-modal-body-message">
<p>Slot message</p>
</div>
<!--v-if-->
</div>
<div class="oc-modal-body-actions oc-flex oc-flex-right">
<oc-button-stub appearance="outline" class="oc-modal-body-actions-cancel" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" style="min-width: 0px;" submit="button" type="button" variation="passive">Cancel</oc-button-stub>
<!--v-if-->
<oc-button-stub appearance="filled" class="oc-modal-body-actions-confirm oc-ml-s" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" style="min-width: 0px;" submit="button" type="button" variation="primary">Confirm</oc-button-stub>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ export class ResourceConflict extends ConflictDialog {
const resolvedConflict: ResolveConflict = await this.resolveFileExists(
{ name: conflict.name, isFolder } as Resource,
conflictsLeft,
conflictsLeft === 1,
isFolder,
true
)
Expand Down
133 changes: 133 additions & 0 deletions packages/web-pkg/src/components/Modals/ResourceConflictModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<template>
<span v-text="message" />
<div class="oc-my-m">
<oc-checkbox
v-if="conflictCount > 1"
v-model="checkboxValue"
size="medium"
:label="checkboxLabel"
:aria-label="checkboxLabel"
/>
</div>
<div class="oc-flex oc-flex-right oc-flex-middle oc-mt-m">
<oc-button
class="oc-modal-body-actions-cancel oc-ml-s"
appearance="outline"
variation="passive"
@click="onCancel"
>{{ $gettext('Skip') }}
</oc-button>
<oc-button
class="oc-modal-body-actions-secondary oc-ml-s"
appearance="outline"
variation="passive"
@click="onConfirmSecondary"
>{{ $gettext('Replace') }}
</oc-button>
<oc-button
class="oc-modal-body-actions-confirm oc-ml-s"
appearance="filled"
variation="primary"
@click="onConfirm"
>{{ $gettext('Keep both') }}
</oc-button>
</div>
</template>

<script lang="ts">
import { computed, defineComponent, PropType, ref, unref } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useStore } from '../../composables'
import { Resource } from '@ownclouders/web-client/src'
import { ResolveConflict, ResolveStrategy } from '../../helpers/resource'
export default defineComponent({
name: 'ResourceConflictModal',
props: {
resource: { type: Object as PropType<Resource>, required: true },
conflictCount: { type: Number, required: true },
callbackFn: {
type: Function as PropType<(resolveConflict: ResolveConflict) => void>,
required: true
},
suggestMerge: { type: Boolean, default: true },
separateSkipHandling: { type: Boolean, default: false }
},
setup(props, { expose }) {
const store = useStore()
const { $gettext } = useGettext()
const checkboxValue = ref()
const checkboxLabel = computed(() => {
if (props.conflictCount < 2) {
return ''
}
if (!props.separateSkipHandling) {
return $gettext(
'Apply to all %{count} conflicts',
{ count: props.conflictCount.toString() },
true
)
} else if (props.resource.isFolder) {
return $gettext(
'Apply to all %{count} folders',
{ count: props.conflictCount.toString() },
true
)
} else {
return $gettext(
'Apply to all %{count} files',
{ count: props.conflictCount.toString() },
true
)
}
})
const message = computed(() =>
props.resource.isFolder
? $gettext(
'Folder with name "%{name}" already exists.',
{ name: props.resource.name },
true
)
: $gettext('File with name "%{name}" already exists.', { name: props.resource.name }, true)
)
const onConfirm = async () => {
await store.dispatch('hideModal')
props.callbackFn({
strategy: ResolveStrategy.KEEP_BOTH,
doForAllConflicts: unref(checkboxValue)
})
}
const onConfirmSecondary = async () => {
await store.dispatch('hideModal')
const strategy = props.suggestMerge ? ResolveStrategy.MERGE : ResolveStrategy.REPLACE
props.callbackFn({
strategy,
doForAllConflicts: unref(checkboxValue)
})
}
const onCancel = async () => {
await store.dispatch('hideModal')
props.callbackFn({
strategy: ResolveStrategy.SKIP,
doForAllConflicts: unref(checkboxValue)
})
}
expose({ onConfirm, onConfirmSecondary, onCancel })
return {
message,
checkboxValue,
checkboxLabel,
onConfirm,
onConfirmSecondary,
onCancel
}
}
})
</script>
1 change: 1 addition & 0 deletions packages/web-pkg/src/components/Modals/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as ResourceConflictModal } from './ResourceConflictModal.vue'
1 change: 1 addition & 0 deletions packages/web-pkg/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export * from './AppTemplates'
export * from './ContextActions'
export * from './FilesList'
export * from './Filters'
export * from './Modals'
export * from './SideBar'
export * from './Search'
export * from './Spaces'
Expand Down
Loading

0 comments on commit 923e5c5

Please sign in to comment.