Skip to content

Commit

Permalink
feat(type): enhance component prop type (#1075)
Browse files Browse the repository at this point in the history
  • Loading branch information
mlmoravek authored Oct 24, 2024
1 parent bbe2865 commit 73f3801
Show file tree
Hide file tree
Showing 43 changed files with 1,056 additions and 1,246 deletions.
5 changes: 3 additions & 2 deletions packages/oruga/src/components/collapse/Collapse.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ defineProps({
position: {
type: String as PropType<"top" | "bottom">,
default: () => getOption("collapse.position", "top"),
validator: (value: string) => ["top", "bottom"].indexOf(value) > -1,
validator: (value: string) => ["top", "bottom"].includes(value),
},
// class props (will not be displayed in the docs)
/** Class of the root element */
Expand Down Expand Up @@ -73,7 +73,8 @@ const isOpen = defineModel<boolean>("open", { default: true });
/** Toggle and emit events */
function toggle(): void {
isOpen.value = !isOpen.value;
isOpen.value ? emits("open") : emits("close");
if (isOpen.value) emits("open");
else emits("close");
}
// --- Computed Component Classes ---
Expand Down
12 changes: 6 additions & 6 deletions packages/oruga/src/components/datepicker/props.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { ComponentClass } from "@/types";
import type { DatepickerEvent } from "./types";
import type { SelectProps } from "../select/types";
import type { DropdownProps } from "../dropdown/types";
import type { InputProps } from "../input/types";
import type { DropdownProps } from "../dropdown/props";
import type { SelectProps } from "../select/props";
import type { InputProps } from "../input/props";

type DatepickerType<IsRange, IsMultiple> = IsRange extends true
? [Date, Date] | []
Expand Down Expand Up @@ -267,15 +267,15 @@ type DatepickerClasses = Partial<{
* Class configuration for the internal input component
* @ignore
*/
inputClasses: InputProps;
inputClasses: InputProps<false>;
/**
* Class configuration for the internal dropdown component
* @ignore
*/
dropdownClasses: DropdownProps;
dropdownClasses: DropdownProps<string, false>;
/**
* Class configuration for the internal select component
* @ignore
*/
selectClasses: SelectProps;
selectClasses: SelectProps<number, false>;
}>;
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ exports[`ODatepicker > render correctly 1`] = `
@binding {boolean} active - dropdown active state
@binding {boolean} toggle - toggle active state function
-->
<div class="o-drop__item o-dpck__box" role="listitem" tabindex="0" data-oruga="dropdown-item" aria-selected="false" aria-disabled="false" override="">
<div class="o-dpck__box" role="listitem" tabindex="0" data-oruga="dropdown-item" aria-selected="false" aria-disabled="false">
<!--
@slot Override the label, default is label prop
-->
Expand Down
4 changes: 2 additions & 2 deletions packages/oruga/src/components/datetimepicker/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export type DatetimepickerProps = {
// class props (will not be displayed in the docs)
type DatetimePickerClasses = Partial<{
/** Class of the Datepicker wrapper */
datepickerWrapperClass?: ComponentClass;
datepickerWrapperClass: ComponentClass;
/** Class of the Timepicker wrapper */
timepickerWrapperClass?: ComponentClass;
timepickerWrapperClass: ComponentClass;
}>;
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ exports[`ODatetimepicker tests > render correctly 1`] = `
@binding {boolean} active - dropdown active state
@binding {boolean} toggle - toggle active state function
-->
<div class="o-drop__item o-dpck__box" role="listitem" tabindex="0" data-oruga="dropdown-item" aria-selected="false" aria-disabled="false" override="">
<div class="o-dpck__box" role="listitem" tabindex="0" data-oruga="dropdown-item" aria-selected="false" aria-disabled="false">
<!--
@slot Override the label, default is label prop
-->
Expand Down Expand Up @@ -335,7 +335,7 @@ exports[`ODatetimepicker tests > render correctly 1`] = `
@binding {boolean} active - dropdown active state
@binding {boolean} toggle - toggle active state function
-->
<div class="o-drop__item o-tpck__box" role="listitem" tabindex="0" data-oruga="dropdown-item" aria-selected="false" aria-disabled="false" override="">
<div class="o-tpck__box" role="listitem" tabindex="0" data-oruga="dropdown-item" aria-selected="false" aria-disabled="false">
<!--
@slot Override the label, default is label prop
-->
Expand Down
3 changes: 2 additions & 1 deletion packages/oruga/src/components/dropdown/Dropdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,8 @@ function selectItem(value: T): void {
// Provided data is a computed ref to enjure reactivity.
const provideData = computed<DropdownComponent<T>>(() => ({
props,
disabled: props.disabled,
multiple: props.multiple || false,
selected: vmodel.value,
selectItem,
}));
Expand Down
89 changes: 23 additions & 66 deletions packages/oruga/src/components/dropdown/DropdownItem.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<script setup lang="ts" generic="T extends string | number | object">
import { useId, computed, type PropType } from "vue";
import { useId, computed } from "vue";
import { getOption } from "@/utils/config";
import { isEqual, isTrueish } from "@/utils/helpers";
import { isDefined, isEqual } from "@/utils/helpers";
import { defineClasses, useProviderChild } from "@/composables";
import type { DynamicComponent } from "@/types";
import type { DropdownComponent } from "./types";
import type { ComponentClass, DynamicComponent } from "@/types";
import type { DropdownItemProps } from "./props";
/**
* @displayName Dropdown Item
Expand All @@ -17,60 +18,19 @@ defineOptions({
configField: "dropdown",
});
const props = defineProps({
/**
* Item value (it will be used as v-model of wrapper component) - default is an uuid
* @type string|number|object
*/
value: {
type: [String, Number, Object] as PropType<T>,
default: () => useId(),
},
/** Item label, unnecessary when default slot is used */
label: { type: String, default: undefined },
/** Item is disabled */
disabled: { type: Boolean, default: false },
/** Item is clickable and emit an event */
clickable: { type: Boolean, default: true },
/** Dropdown item tag name */
tag: {
type: [String, Object, Function] as PropType<DynamicComponent>,
default: () => getOption<DynamicComponent>("dropdown.itemTag", "div"),
},
/** Set the tabindex attribute on the dropdown item div (-1 to prevent selection via tab key) */
tabindex: { type: [Number, String], default: 0 },
/**
* Role attribute to be passed to the list item for better accessibility.
* Use menuitem only in situations where your dropdown is related to a navigation menu.
* @values listitem, menuitem, button
*/
ariaRole: {
type: String,
default: () => getOption("dropdown.itemAriaRole", "listitem"),
},
// class props (will not be displayed in the docs)
/** Class of the dropdown item */
itemClass: {
type: [String, Array, Function] as PropType<ComponentClass>,
default: undefined,
},
/** Class of the dropdown item when active */
itemActiveClass: {
type: [String, Array, Function] as PropType<ComponentClass>,
default: undefined,
},
/** Class of the dropdown item when clickable */
itemClickableClass: {
type: [String, Array, Function] as PropType<ComponentClass>,
default: undefined,
},
/** Class of the dropdown item when disabled */
itemDisabledClass: {
type: [String, Array, Function] as PropType<ComponentClass>,
default: undefined,
},
const props = withDefaults(defineProps<DropdownItemProps<T>>(), {
override: undefined,
value: undefined,
label: undefined,
disabled: false,
clickable: true,
tag: () => getOption<DynamicComponent>("dropdown.itemTag", "div"),
tabindex: 0,
ariaRole: () => getOption("dropdown.itemAriaRole", "listitem"),
});
const itemValue = props.value || useId();
const emits = defineEmits<{
/**
* onclick event
Expand All @@ -84,26 +44,23 @@ const emits = defineEmits<{
const { parent } = useProviderChild<DropdownComponent<T>>();
const isClickable = computed(
() => !parent.value.props.disabled && !props.disabled && props.clickable,
() => !parent.value.disabled && !props.disabled && props.clickable,
);
const isActive = computed(() => {
if (parent.value.selected === undefined) return false;
if (
isTrueish(parent.value.props.multiple) &&
Array.isArray(parent.value.selected)
)
if (!isDefined(parent.value.selected)) return false;
if (parent.value.multiple && Array.isArray(parent.value.selected))
return parent.value.selected.some((selected: T) =>
isEqual(props.value, selected),
isEqual(itemValue, selected),
);
return isEqual(props.value, parent.value.selected);
return isEqual(itemValue, parent.value.selected);
});
/** Click listener, select the item. */
function selectItem(event: Event): void {
if (!isClickable.value) return;
parent.value.selectItem(props.value as T);
emits("click", props.value as T, event);
parent.value.selectItem(itemValue as T);
emits("click", itemValue as T, event);
}
// --- Computed Component Classes ---
Expand All @@ -114,7 +71,7 @@ const rootClasses = defineClasses(
"itemDisabledClass",
"o-drop__item--disabled",
null,
computed(() => parent.value.props.disabled || props.disabled),
computed(() => parent.value.disabled || props.disabled),
],
["itemActiveClass", "o-drop__item--active", null, isActive],
["itemClickableClass", "o-drop__item--clickable", null, isClickable],
Expand Down
38 changes: 38 additions & 0 deletions packages/oruga/src/components/dropdown/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,41 @@ type DropdownClasses = Partial<{
/** Class of the body when dropdown is open and scroll is not clip */
noScrollClass: ComponentClass;
}>;

export type DropdownItemProps<T extends string | number | object> = {
/** Override existing theme classes completely */
override?: boolean;
/**
* Item value (it will be used as v-model of wrapper component) - default is an uuid
* @type string|number|object
*/
value?: T;
/** Item label, unnecessary when default slot is used */
label?: string;
/** Item is disabled */
disabled?: boolean;
/** Item is clickable and emit an event */
clickable?: boolean;
/** Dropdown item tag name */
tag?: DynamicComponent;
/** Set the tabindex attribute on the dropdown item div (-1 to prevent selection via tab key) */
tabindex?: number | string;
/**
* Role attribute to be passed to the list item for better accessibility.
* Use menuitem only in situations where your dropdown is related to a navigation menu.
* @values listitem, menuitem, button
*/
ariaRole?: string;
} & DropdownItemClasses;

// class props (will not be displayed in the docs)
type DropdownItemClasses = Partial<{
/** Class of the dropdown item */
itemClass: ComponentClass;
/** Class of the dropdown item when active */
itemActiveClass: ComponentClass;
/** Class of the dropdown item when clickable */
itemClickableClass: ComponentClass;
/** Class of the dropdown item when disabled */
itemDisabledClass: ComponentClass;
}>;
13 changes: 4 additions & 9 deletions packages/oruga/src/components/dropdown/types.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import type { ComponentProps } from "vue-component-type-helpers";

import Dropdown from "./Dropdown.vue";

export type DropdownProps = ComponentProps<typeof Dropdown>;

export type DropdownComponent<T> = {
props: DropdownProps;
selected?: T | T[];
export type DropdownComponent<T extends string | number | object> = {
disabled: boolean;
multiple: boolean;
selected: T | T[] | undefined;
selectItem: (value: T) => void;
};
Loading

0 comments on commit 73f3801

Please sign in to comment.