### Major Features - Add comprehensive ORCID validation with checksum verification - Implement unsaved changes detection and auto-save functionality - Enhanced form component reactivity and state management ### ORCID Implementation - Create custom VineJS ORCID validation rule with MOD-11-2 algorithm - Add ORCID fields to Person model and TablePersons component - Update dataset validators to include ORCID validation - Add descriptive placeholder text for ORCID input fields ### UI/UX Improvements - Add UnsavedChangesWarning component with detailed change tracking - Improve FormCheckRadio and FormCheckRadioGroup reactivity - Enhanced BaseButton with proper disabled state handling - Better error handling and user feedback in file validation ### Data Management - Implement sophisticated change detection for all dataset fields - Add proper handling of array ordering for authors/contributors - Improve license selection with better state management - Enhanced subject/keyword processing with duplicate detection ### Technical Improvements - Optimize search indexing with conditional updates based on modification dates - Update person model column mapping for ORCID - Improve validation error messages and user guidance - Better handling of file uploads and deletion tracking ### Dependencies - Update various npm packages (AWS SDK, Babel, Vite, etc.) - Add baseline-browser-mapping for better browser compatibility ### Bug Fixes - Fix form reactivity issues with checkbox/radio groups - Improve error handling in file validation rules - Better handling of edge cases in change detection
182 lines
4.5 KiB
Vue
182 lines
4.5 KiB
Vue
<script lang="ts" setup>
|
|
import { computed, PropType } from 'vue';
|
|
import { Link } from '@inertiajs/vue3';
|
|
// import { Link } from '@inertiajs/inertia-vue3';
|
|
import { getButtonColor } from '@/colors';
|
|
import BaseIcon from '@/Components/BaseIcon.vue';
|
|
|
|
const props = defineProps({
|
|
label: {
|
|
type: [String, Number],
|
|
default: null,
|
|
},
|
|
icon: {
|
|
type: String,
|
|
default: null,
|
|
},
|
|
href: {
|
|
type: String,
|
|
default: null,
|
|
},
|
|
target: {
|
|
type: String,
|
|
default: null,
|
|
},
|
|
routeName: {
|
|
type: String,
|
|
default: null,
|
|
},
|
|
type: {
|
|
type: String,
|
|
default: null,
|
|
},
|
|
color: {
|
|
type: String as PropType<'white' | 'contrast' | 'light' | 'success' | 'danger' | 'warning' | 'info' | 'modern'>,
|
|
default: 'white',
|
|
},
|
|
as: {
|
|
type: String,
|
|
default: null,
|
|
},
|
|
small: Boolean,
|
|
outline: Boolean,
|
|
active: Boolean,
|
|
disabled: Boolean,
|
|
roundedFull: Boolean,
|
|
});
|
|
|
|
const emit = defineEmits(['click']);
|
|
|
|
const is = computed(() => {
|
|
if (props.as) {
|
|
return props.as;
|
|
}
|
|
|
|
// If disabled, always render as button or span to prevent navigation
|
|
if (props.disabled) {
|
|
return props.routeName || props.href ? 'span' : 'button';
|
|
}
|
|
|
|
if (props.routeName) {
|
|
return Link;
|
|
}
|
|
|
|
if (props.href) {
|
|
return 'a';
|
|
}
|
|
|
|
return 'button';
|
|
});
|
|
|
|
const computedType = computed(() => {
|
|
if (is.value === 'button') {
|
|
return props.type ?? 'button';
|
|
}
|
|
|
|
return null;
|
|
});
|
|
|
|
// Only provide href/routeName when not disabled
|
|
const computedHref = computed(() => {
|
|
if (props.disabled) return null;
|
|
return props.routeName || props.href;
|
|
});
|
|
|
|
// Only provide target when not disabled and has href
|
|
const computedTarget = computed(() => {
|
|
if (props.disabled || !props.href) return null;
|
|
return props.target;
|
|
});
|
|
|
|
// Only provide disabled attribute for actual button elements
|
|
const computedDisabled = computed(() => {
|
|
if (is.value === 'button') {
|
|
return props.disabled;
|
|
}
|
|
return null;
|
|
});
|
|
|
|
const labelClass = computed(() => (props.small && props.icon ? 'px-1' : 'px-2'));
|
|
|
|
const componentClass = computed(() => {
|
|
const base = [
|
|
'inline-flex',
|
|
'justify-center',
|
|
'items-center',
|
|
'whitespace-nowrap',
|
|
'focus:outline-none',
|
|
'transition-colors',
|
|
'duration-150',
|
|
'border',
|
|
props.roundedFull ? 'rounded-full' : 'rounded',
|
|
];
|
|
|
|
// Only add focus ring styles when not disabled
|
|
if (!props.disabled) {
|
|
base.push('focus:ring-2');
|
|
base.push(props.active ? 'ring ring-black dark:ring-white' : 'ring-blue-700');
|
|
}
|
|
|
|
// Add button colors
|
|
// Add button colors - handle both string and array returns
|
|
// const buttonColors = getButtonColor(props.color, props.outline, !props.disabled);
|
|
base.push(getButtonColor(props.color, props.outline, !props.disabled));
|
|
// if (Array.isArray(buttonColors)) {
|
|
// base.push(...buttonColors);
|
|
// } else {
|
|
// base.push(buttonColors);
|
|
// }
|
|
|
|
// Add size classes
|
|
if (props.small) {
|
|
base.push('text-sm', props.roundedFull ? 'px-3 py-1' : 'p-1');
|
|
} else {
|
|
base.push('py-2', props.roundedFull ? 'px-6' : 'px-3');
|
|
}
|
|
|
|
// Add disabled/enabled specific classes
|
|
if (props.disabled) {
|
|
base.push(
|
|
'cursor-not-allowed',
|
|
'opacity-60',
|
|
'pointer-events-none', // This prevents all interactions
|
|
);
|
|
} else {
|
|
base.push('cursor-pointer');
|
|
// Add hover effects only when not disabled
|
|
if (is.value === 'button' || is.value === 'a' || is.value === Link) {
|
|
base.push('hover:opacity-80');
|
|
}
|
|
}
|
|
|
|
return base;
|
|
});
|
|
|
|
// Handle click events with disabled check
|
|
const handleClick = (event) => {
|
|
if (props.disabled) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return;
|
|
}
|
|
emit('click', event);
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<component
|
|
:is="is"
|
|
:class="componentClass"
|
|
:href="computedHref"
|
|
:to="props.disabled ? null : props.routeName"
|
|
:type="computedType"
|
|
:target="computedTarget"
|
|
:disabled="computedDisabled"
|
|
:tabindex="props.disabled ? -1 : null"
|
|
:aria-disabled="props.disabled ? 'true' : null"
|
|
@click="handleClick"
|
|
>
|
|
<BaseIcon v-if="icon" :path="icon" />
|
|
<span v-if="label" :class="labelClass">{{ label }}</span>
|
|
</component>
|
|
</template>
|