Skip to content

Commit

Permalink
feat: Set merge strategy per component on app.config
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisGV04 committed Feb 17, 2024
1 parent 2b91eff commit 645006f
Show file tree
Hide file tree
Showing 14 changed files with 70 additions and 86 deletions.
5 changes: 1 addition & 4 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
{
"tailwindCSS.experimental.classRegex": [
["ui:\\s*{([^)]*)\\s*}", "[\"'`]([^\"'`]*).*?[\"'`]"],
["/\\*ui\\*/\\s*{([^;]*)}", ":\\s*[\"'`]([^\"'`]*).*?[\"'`]"]
]
"tailwindCSS.experimental.classRegex": [["/\\*ui\\*/\\s*{([^;]*)}", ":\\s*[\"'`]([^\"'`]*).*?[\"'`]"]]
}
8 changes: 2 additions & 6 deletions docs/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ import { defineAppConfig } from '#imports';

export default defineAppConfig({
ui: /*ui*/ {
container: {
constrained: 'max-w-2xl',
},
container: { constrained: 'max-w-2xl' },

button: {
variant: {
Expand All @@ -18,9 +16,7 @@ export default defineAppConfig({
'text-primary-500 underline-offset-4 hover:text-primary-600 hover:underline focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500 disabled:text-primary-500',
},

default: {
variant: 'primary-solid',
},
default: { variant: 'primary-solid' },
},
},
});
13 changes: 4 additions & 9 deletions src/module.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import type { CollectionNames, IconsPluginOptions } from '@egoist/tailwindcss-icons';
import { getIconCollections, iconsPlugin } from '@egoist/tailwindcss-icons';
import { addComponentsDir, addImportsDir, createResolver, defineNuxtModule, installModule } from '@nuxt/kit';
import { addComponentsDir, createResolver, defineNuxtModule, installModule } from '@nuxt/kit';
import twForms from '@tailwindcss/forms';
import twAnimate from 'tailwindcss-animate';
import { name, version } from '../package.json';
import type { DeepPartial, Strategy } from './runtime/types';
import * as config from './runtime/ui.config';

type UI = {
strategy?: Strategy;
icons?: { dynamic: boolean };
} & DeepPartial<typeof config>;
/** Adds strategy to every key of the object */
type WithStrategy<Config extends object> = { [Key in keyof Config]: Config[Key] & { strategy?: Strategy } };
type UI = { icons?: { dynamic: boolean } } & DeepPartial<WithStrategy<typeof config>>;

declare module 'nuxt/schema' {
interface AppConfigInput {
Expand Down Expand Up @@ -125,9 +124,5 @@ export default defineNuxtModule<ModuleOptions>({
global: options.global,
watch: false,
});

// Composables

addImportsDir(resolve(runtimeDir, 'composables'));
},
});
4 changes: 2 additions & 2 deletions src/runtime/components/elements/Badge.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
// @ts-expect-error
import appConfig from '#build/app.config';
import { badge } from '#ui/ui.config';
import { mergeConfig } from '#ui/utils';
import { badge } from '../../ui.config';
const config = mergeConfig<typeof badge>(appConfig.ui.strategy, appConfig.ui.badge, badge);
const config = mergeConfig<typeof badge>(appConfig.ui?.badge?.strategy, appConfig.ui?.badge, badge);
type UiConfig = Partial<typeof config> & { strategy?: Strategy };
</script>

Expand Down
4 changes: 2 additions & 2 deletions src/runtime/components/elements/Button.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
// @ts-expect-error
import appConfig from '#build/app.config';
import { button } from '#ui/ui.config';
import { mergeConfig } from '#ui/utils';
import { button } from '../../ui.config';
const config = mergeConfig<typeof button>(appConfig.ui.strategy, appConfig.ui.button, button);
const config = mergeConfig<typeof button>(appConfig.ui?.button?.strategy, appConfig.ui?.button, button);
type UiConfig = Partial<typeof config> & { strategy?: Strategy };
</script>

Expand Down
8 changes: 6 additions & 2 deletions src/runtime/components/elements/Dropdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
// @ts-expect-error
import appConfig from '#build/app.config';
import { dropdown } from '#ui/ui.config';
import { mergeConfig } from '#ui/utils';
import dropdown from '../../ui.config/dropdown';
const config = mergeConfig<typeof dropdown>(appConfig.ui.strategy, appConfig.ui.dropdown, dropdown);
const config = mergeConfig<typeof dropdown>(
appConfig.ui?.dropdown?.strategy,
appConfig.ui?.dropdown,
dropdown,
);
type UiConfig = Partial<typeof config> & { strategy?: Strategy };
</script>

Expand Down
6 changes: 5 additions & 1 deletion src/runtime/components/forms/FormInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import appConfig from '#build/app.config';
import { formInput } from '#ui/ui.config';
import { mergeConfig } from '#ui/utils';
const config = mergeConfig<typeof formInput>(appConfig.ui.strategy, appConfig.ui.formInput, formInput);
const config = mergeConfig<typeof formInput>(
appConfig.ui?.formInput?.strategy,
appConfig.ui?.formInput,
formInput,
);
type UiConfig = Partial<typeof config> & { strategy?: Strategy };
</script>

Expand Down
10 changes: 3 additions & 7 deletions src/runtime/components/forms/FormInputLabel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { formInputLabel } from '#ui/ui.config';
import { mergeConfig } from '#ui/utils';
const config = mergeConfig<typeof formInputLabel>(
appConfig.ui.strategy,
appConfig.ui.formInputLabel,
appConfig.ui?.formInputLabel?.strategy,
appConfig.ui?.formInputLabel,
formInputLabel,
);
type UiConfig = Partial<typeof config> & { strategy?: Strategy };
Expand All @@ -21,11 +21,7 @@ import { toRef, withDefaults } from 'vue';
const props = withDefaults(
defineProps<LabelProps & { mandatory?: boolean; error?: boolean; class?: any; ui?: UiConfig }>(),
{
as: 'label',
class: undefined,
ui: () => ({}) as UiConfig,
},
{ as: 'label', class: undefined, ui: () => ({}) as UiConfig },
);
const { ui, attrs } = useUI('formInputLabel', toRef(props, 'ui'), config);
Expand Down
8 changes: 6 additions & 2 deletions src/runtime/components/layout/Container.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
// @ts-expect-error
import appConfig from '#build/app.config';
import { container } from '#ui/ui.config';
import { mergeConfig } from '#ui/utils';
import { container } from '../../ui.config';
const config = mergeConfig<typeof container>(appConfig.ui.strategy, appConfig.ui.container, container);
const config = mergeConfig<typeof container>(
appConfig.ui.container.strategy,
appConfig.ui.container,
container,
);
type UiConfig = Partial<typeof config> & { strategy?: Strategy };
</script>

Expand Down
4 changes: 2 additions & 2 deletions src/runtime/components/overlays/Dialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
// @ts-expect-error
import appConfig from '#build/app.config';
import { dialog } from '#ui/ui.config';
import { mergeConfig } from '#ui/utils';
import dialog from '../../ui.config/dialog';
const config = mergeConfig<typeof dialog>(appConfig.ui.strategy, appConfig.ui.dialog, dialog);
const config = mergeConfig<typeof dialog>(appConfig.ui?.dialog?.strategy, appConfig.ui?.dialog, dialog);
type UiConfig = Partial<typeof config> & { strategy?: Strategy };
</script>

Expand Down
8 changes: 6 additions & 2 deletions src/runtime/components/overlays/Slideover.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
// @ts-expect-error
import appConfig from '#build/app.config';
import { slideover } from '#ui/ui.config';
import { mergeConfig } from '#ui/utils';
import slideover from '../../ui.config/slideover';
const config = mergeConfig<typeof slideover>(appConfig.ui.strategy, appConfig.ui.slideover, slideover);
const config = mergeConfig<typeof slideover>(
appConfig.ui?.slideover?.strategy,
appConfig.ui?.slideover,
slideover,
);
type UiConfig = Partial<typeof config> & { strategy?: Strategy };
</script>

Expand Down
4 changes: 2 additions & 2 deletions src/runtime/components/overlays/Tooltip.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
// @ts-expect-error
import appConfig from '#build/app.config';
import { tooltip } from '#ui/ui.config';
import { mergeConfig } from '#ui/utils';
import tooltip from '../../ui.config/tooltip';
const config = mergeConfig<typeof tooltip>(appConfig.ui.strategy, appConfig.ui.tooltip, tooltip);
const config = mergeConfig<typeof tooltip>(appConfig.ui?.tooltip?.strategy, appConfig.ui?.tooltip, tooltip);
type UiConfig = Partial<typeof config> & { strategy?: Strategy };
</script>

Expand Down
35 changes: 16 additions & 19 deletions src/runtime/composables/useUI.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,36 @@
import { useAppConfig } from "#imports";
import omit from "just-omit";
import get from "just-safe-get";
import type { Ref } from "vue";
import { computed, toValue, useAttrs } from "vue";
import type { Strategy } from "../types";
import { mergeConfig } from "../utils";
import { useAppConfig } from '#imports';
import omit from 'just-omit';
import get from 'just-safe-get';
import { computed, toValue, useAttrs, type Ref } from 'vue';
import type { Strategy } from '../types';
import { mergeConfig } from '../utils';

/**
* Internal composable that helps merging `app.config.ts` and component `ui` classes.
*
* It returns the merged `ui` configs and the `attrs` without the `class` to avoid clashes
*/
export const useUI = <T>(
key,
key: string,
$ui: Ref<Partial<T & { strategy: Strategy }> | undefined>,
$config?: Ref<T> | T,
$wrapperClass?: Ref<string>,
withAppConfig: boolean = false,
) => {
const $attrs = useAttrs();
const appConfig = useAppConfig();

const ui = computed(() => {
const _ui = toValue($ui);
const _config = toValue($config);
const _wrapperClass = toValue($wrapperClass);

return mergeConfig<T>(
_ui?.strategy || (appConfig.ui?.strategy as Strategy),
_wrapperClass ? { wrapper: _wrapperClass } : {},
_ui?.strategy || 'merge',
_ui || {},
process.dev || withAppConfig ? get(appConfig.ui, key, {}) : {},
process.dev ? get(appConfig.ui, key, {}) : {},
_config || {},
);
});

const attrs = computed(() => omit($attrs, ["class"]));
const attrs = computed(() => omit($attrs, ['class']));

return {
ui,
attrs,
};
return { ui, attrs };
};
39 changes: 13 additions & 26 deletions src/runtime/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createDefu, defu } from "defu";
import { extendTailwindMerge } from "tailwind-merge";
import type { Strategy } from "../types";
import { createDefu, defu } from 'defu';
import { extendTailwindMerge } from 'tailwind-merge';
import type { Strategy } from '../types';

const customTwMerge = extendTailwindMerge<string, string>({
extend: {
Expand All @@ -11,23 +11,18 @@ const customTwMerge = extendTailwindMerge<string, string>({
});

const defuTwMerge = createDefu((obj, key, value, namespace) => {
if (namespace === "default" || namespace.startsWith("default.")) {
if (namespace === 'default' || namespace.startsWith('default.')) {
return false;
}
if (
typeof obj[key] === "string" &&
typeof value === "string" &&
obj[key] &&
value
) {
if (typeof obj[key] === 'string' && typeof value === 'string' && obj[key] && value) {
// @ts-ignore
obj[key] = customTwMerge(obj[key], value);
return true;
}
});

export function mergeConfig<T>(strategy: Strategy, ...configs): T {
if (strategy === "override") {
export function mergeConfig<T>(strategy: Strategy = 'merge', ...configs): T {
if (strategy === 'override') {
return defu({}, ...configs) as T;
}

Expand All @@ -42,26 +37,21 @@ export function hexToRgb(hex: string) {
});

const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result
? `${parseInt(result[1], 16)} ${parseInt(result[2], 16)} ${parseInt(
result[3],
16,
)}`
: null;
return result ? `${parseInt(result[1], 16)} ${parseInt(result[2], 16)} ${parseInt(result[3], 16)}` : null;
}

export function getSlotsChildren(slots: any) {
let children = slots.default?.();
if (children.length) {
children = children
.flatMap((c: any) => {
if (typeof c.type === "symbol") {
if (typeof c.children === "string") {
if (typeof c.type === 'symbol') {
if (typeof c.children === 'string') {
// `v-if="false"` or commented node
return;
}
return c.children;
} else if (c.type.name === "ContentSlot") {
} else if (c.type.name === 'ContentSlot') {
return c.ctx.slots.default?.();
}
return c;
Expand All @@ -83,16 +73,13 @@ export const isDeepEqual = (object1: any, object2: any): boolean => {

const areObjects = isObject(value1) && isObject(value2);

if (
(areObjects && !isDeepEqual(value1, value2)) ||
(!areObjects && value1 !== value2)
) {
if ((areObjects && !isDeepEqual(value1, value2)) || (!areObjects && value1 !== value2)) {
return false;
}
}
return true;
};

export const isObject = (input: unknown): input is object => {
return input != null && typeof input === "object";
return input != null && typeof input === 'object';
};

0 comments on commit 645006f

Please sign in to comment.