Skip to content

Commit

Permalink
fix: switch can't toggle.
Browse files Browse the repository at this point in the history
  • Loading branch information
chizuki committed Feb 16, 2023
1 parent 30f84c7 commit 29185d2
Show file tree
Hide file tree
Showing 10 changed files with 244 additions and 77 deletions.
2 changes: 1 addition & 1 deletion packages/hoci/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"@vueuse/core": "^9.12.0",
"maybe-types": "^0.0.3"
},
"devDependencies": {
"peerDependencies": {
"vue": "^3.2.31"
}
}
1 change: 0 additions & 1 deletion packages/hoci/src/components/selection/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ export const HiItem = defineComponent({
props,
context
);

return () =>
h(
"div",
Expand Down
65 changes: 51 additions & 14 deletions packages/hoci/src/components/selection/list.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,32 @@
import { isDefined, syncRef } from "@vueuse/core";
import type { PropType } from "vue";
import { computed, defineComponent, h, provide, reactive, renderSlot } from "vue";
import {
computed,
defineComponent,
h,
provide,
reactive,
renderSlot
} from "vue";
import type { ActivateEvent } from "../../types";
import { classPropType, labelPropType, valuePropType } from "../../constants";
import { defineHookComponent, defineHookEmits, defineHookProps } from "../../shared";
import {
defineHookComponent,
defineHookEmits,
defineHookProps,
normalizeClass
} from "../../shared";
import type { Option } from "./constants";
import { ActivateEventSymbol, ActiveClassSymbol, ChangeActiveSymbol, InitSymbol, IsActiveSymbol, ItemClassSymbol, ItemLabelSymbol, UnactiveSymbol } from "./constants";
import {
ActivateEventSymbol,
ActiveClassSymbol,
ChangeActiveSymbol,
InitSymbol,
IsActiveSymbol,
ItemClassSymbol,
ItemLabelSymbol,
UnactiveSymbol
} from "./constants";

export const selectionListProps = defineHookProps({
tag: {
Expand Down Expand Up @@ -61,7 +82,12 @@ export const selectionListProps = defineHookProps({
}
});

export const selectionListEmits = defineHookEmits(["update:modelValue", "change", "load", "unload"]);
export const selectionListEmits = defineHookEmits([
"update:modelValue",
"change",
"load",
"unload"
]);

export const useSelectionList = defineHookComponent({
props: selectionListProps,
Expand All @@ -74,12 +100,14 @@ export const useSelectionList = defineHookComponent({
return [];
}
if (props.multiple && Array.isArray(value)) {
return value.filter(v => v != null || v !== undefined);
return value.filter((v) => v != null || v !== undefined);
}
return [value];
}

const actives: any[] = reactive<any[]>([...toArray(props.modelValue ?? props.defaultValue)]);
const actives: any[] = reactive<any[]>([
...toArray(props.modelValue ?? props.defaultValue)
]);

const currentValue = computed({
get() {
Expand All @@ -103,22 +131,28 @@ export const useSelectionList = defineHookComponent({

provide(
ActiveClassSymbol,
computed(() => props.activeClass)
computed(() => normalizeClass(props.activeClass))
);

provide(
UnactiveSymbol,
computed(() => props.unactiveClass)
computed(() => normalizeClass(props.unactiveClass))
);

provide(
ItemClassSymbol,
computed(() => props.itemClass)
computed(() => normalizeClass(props.itemClass))
);

provide(ItemLabelSymbol, computed(() => props.label));
provide(
ItemLabelSymbol,
computed(() => props.label)
);

provide(ActivateEventSymbol, computed(() => props.activateEvent));
provide(
ActivateEventSymbol,
computed(() => props.activateEvent)
);

const emitChange = () => emit("change", currentValue.value);

Expand All @@ -134,7 +168,8 @@ export const useSelectionList = defineHookComponent({
}
} else {
if (props.multiple) {
const limit = typeof props.multiple === "number" ? props.multiple : Infinity;
const limit
= typeof props.multiple === "number" ? props.multiple : Infinity;
if (actives.length < limit) {
actives.push(option);
emitChange();
Expand All @@ -152,7 +187,7 @@ export const useSelectionList = defineHookComponent({

provide(InitSymbol, (option: Option) => {
function remove() {
const index = options.findIndex(e => e.id === option.id);
const index = options.findIndex((e) => e.id === option.id);
if (index > -1) {
options.splice(index, 1);
emit("unload", option);
Expand All @@ -170,7 +205,9 @@ export const useSelectionList = defineHookComponent({
});

function renderItem() {
const children = options.filter(e => actives.includes(e.value)).map(e => e.render());
const children = options
.filter((e) => actives.includes(e.value))
.map((e) => e.render());
return props.multiple ? children : children[0];
}

Expand Down
35 changes: 6 additions & 29 deletions packages/hoci/src/components/switch.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,15 @@
import type { PropType } from "vue";
import {
capitalize,
computed,
defineComponent,
h,
ref,
renderSlot,
watch
} from "vue";
import { capitalize, computed, defineComponent, h, renderSlot } from "vue";
import { useVModel } from "@vueuse/core";
import {
defineHookComponent,
defineHookEmits,
defineHookProps
} from "../shared";
import type { ActivateEvent } from "../types";
import { classPropType } from "../constants";
import { selectionItemProps } from "../../dist";

export const switchProps = defineHookProps({
...selectionItemProps,
modelValue: {
type: Boolean,
default: false
Expand Down Expand Up @@ -59,23 +50,9 @@ export const useSwitch = defineHookComponent({
props: switchProps,
emits: switchEmits,
setup(props, context) {
const checked = ref(!!props.modelValue);

watch(
() => props.modelValue,
(val) => {
checked.value = !!val;
}
);

const modelValue = computed<boolean>({
get() {
return !!(props.modelValue ?? checked.value);
},
set(val) {
checked.value = val;
context.emit("update:modelValue", val);
}
const modelValue = useVModel(props, "modelValue", context.emit, {
passive: true,
defaultValue: false
});

const toggle = function (value?: any) {
Expand All @@ -85,8 +62,8 @@ export const useSwitch = defineHookComponent({
const oldValue = modelValue.value;
const newValue = typeof value === "boolean" ? value : !oldValue;
if (newValue !== oldValue) {
modelValue.value = newValue;
context.emit("change", newValue);
context.emit("update:modelValue", newValue);
}
};

Expand Down
16 changes: 13 additions & 3 deletions packages/hoci/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import type { PropType } from "vue";

export const valuePropType = [String, Number, Object, Boolean, null] as PropType<any>;
export const valuePropType = [
String,
Number,
Object,
Boolean,
null
] as PropType<any>;

export const classPropType = [String, Array] as PropType<string | string[]>;
export const classPropType = [String, Array, Object] as PropType<
string | string[] | Record<string, boolean>
>;

export const labelPropType = [String, Function] as PropType<string | ((val?: any) => string) | null>;
export const labelPropType = [String, Function] as PropType<
string | ((val?: any) => string) | null
>;
122 changes: 113 additions & 9 deletions packages/hoci/src/shared.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,131 @@
import type { ComponentPropsOptions, EmitsOptions, ExtractPropTypes, SetupContext } from "vue";
import type { MaybeFunction } from "maybe-types";
import type {
ComponentObjectPropsOptions,
ComponentPropsOptions,
EmitsOptions,
ExtractDefaultPropTypes,
ExtractPropTypes,
PropType,
SetupContext
} from "vue";
import { reactiveComputed } from "@vueuse/core";
import type { ClassType } from "./types";

export interface HookComponentOptions<R, E = EmitsOptions, EE extends string = string, P = ComponentPropsOptions, D extends Record<string, unknown> = ExtractPropTypes<P>> {
export interface HookComponentOptions<
R,
E = EmitsOptions,
EE extends string = string,
P = ComponentPropsOptions,
D = ExtractPropTypes<P>
> {
props?: P
emits?: E | EE[]
setup: (props: D, context: SetupContext<E>) => R
}

export type HookComponent<R, E = EmitsOptions, P = ComponentPropsOptions, D extends Record<string, unknown> = ExtractPropTypes<P>> = (props: MaybeFunction<D>, context: SetupContext<E>) => R;
export type HookComponent<
R,
E = EmitsOptions,
P = ComponentPropsOptions,
D = ExtractPropTypes<P>,
Defaults = ExtractDefaultPropTypes<P>
> = (
props: MaybeFunction<Partial<Defaults> & Omit<D, keyof Defaults>>,
context: SetupContext<E>
) => R & { $props: D };

export function defineHookProps<P = ComponentPropsOptions>(props: P) {
export function defineHookProps<
P extends ComponentObjectPropsOptions = ComponentObjectPropsOptions
>(props: P) {
return props;
}

export function defineHookEmits<E, EE extends string = string>(emits: E | EE[]) {
export function defineHookEmits<
E extends EmitsOptions = EmitsOptions,
EE extends string = string
>(emits: E | EE[]) {
return emits;
}

export function defineHookComponent<R, E = EmitsOptions, EE extends string = string, P = ComponentPropsOptions, D extends Record<string, unknown> = ExtractPropTypes<P>>(options: HookComponentOptions<R, E, EE, P, D>): HookComponent<R, E, P, D> {
return (props: MaybeFunction<D>, context: SetupContext<E>) => {
const p = props instanceof Function ? reactiveComputed(() => props()) : props;
return options.setup(p, context);
export function defineHookComponent<
R,
E = EmitsOptions,
EE extends string = string,
P = ComponentPropsOptions,
D = ExtractPropTypes<P>,
Defaults = ExtractDefaultPropTypes<P>
>(
options: HookComponentOptions<R, E, EE, P, D>
): HookComponent<R, E, P, D, Defaults> {
return (
props: MaybeFunction<Partial<Defaults> & Omit<D, keyof Defaults>>,
context: SetupContext<E>
) => {
const p = withDefaults<P, D, Defaults>(
isFunction(props) ? reactiveComputed(() => props()) : props,
options.props!
);
const rs = options.setup(p, context);
return { ...rs, $props: p } as R & { $props: D };
};
}

function isFunction<F extends Function>(value: unknown): value is F {
return typeof value === "function";
}

function withDefaults<
P = ComponentPropsOptions,
D = ExtractPropTypes<P>,
Defaults = ExtractDefaultPropTypes<P>
>(props: Partial<Defaults> & Omit<D, keyof Defaults>, propsOptions: P): D {
if (Array.isArray(propsOptions)) {
return props as D;
}
const rs = {} as D;
const options = propsOptions as ComponentObjectPropsOptions<D>;
for (const key in options) {
const k = key as keyof D;
const opt = options[k];
if (opt === null) {
continue;
}
if (typeof opt === "function") {
if (isExtends(opt, Boolean)) {
rs[key] = false as D[typeof key];
}
continue;
}
if (Array.isArray(opt)) {
if (opt.some((e) => isExtends(e, Boolean))) {
rs[key] = false as D[typeof key];
}
continue;
}
if (isFunction(opt.default)) {
rs[key] = opt.default(props) as D[typeof key];
} else if (opt.default !== undefined) {
rs[key] = opt.default as D[typeof key];
}
}
return { ...rs, ...props } as D;
}

export function isExtends(types: PropType<any>, value: PropType<any>): boolean {
if (Array.isArray(types)) {
return types.some((e) => isExtends(e, value));
}
return value === types;
}

export function normalizeClass(value: ClassType): string {
if (Array.isArray(value)) {
return value.join(" ");
}
if (typeof value === "string") {
return value;
}
return Object.keys(value)
.filter((e) => !!value[e])
.join(" ");
}
12 changes: 10 additions & 2 deletions packages/hoci/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
export type ClassType = string | string[];
export type ClassType = string | string[] | Record<string, any>;

export type ActivateEvent = "click" | "mouseenter" | "mousedown" | "mouseup" | "dblclick" | "contextmenu" | "touchstart" | "touchend";
export type ActivateEvent =
| "click"
| "mouseenter"
| "mousedown"
| "mouseup"
| "dblclick"
| "contextmenu"
| "touchstart"
| "touchend";

export type ElementLike = JSX.Element | string | ElementLike[];
1 change: 1 addition & 0 deletions playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
},
"devDependencies": {
"@types/jquery": "^3.5.14",
"@types/node": "^18.13.0",
"@vitejs/plugin-vue": "^4.0.0",
"@vitejs/plugin-vue-jsx": "^3.0.0",
"compression": "^1.7.4",
Expand Down
Loading

0 comments on commit 29185d2

Please sign in to comment.