-
Notifications
You must be signed in to change notification settings - Fork 180
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Carbon v11 #1587
Comments
#1596 @jeffchew I wrapped cds-button and cds-text-input. Those work pretty well. I would still like to try and generate wrappers automatically so I'll keep exploring that. |
I didn't play too much with web components, the first thing that pops in my mind, is scoped slots, but i think we can workaround it with provide / inject.
Other than that i think its a good direction. I ran through your commits, i like the idea of using vite. For wrapper generation, lit labs have something like that, but i didn't tried it yet - gen-wrapper-vue |
Hey @davidnixon, i played a little with this lib, its not near an automated wrapper generator, but it can definitely can generate types for props and such. I checked out the carbon repository, and wired-in the lib. package.json ...
"scripts": {
"generate-vue-types": "custom-elements-manifest analyze && node generate-vue-types.js",
...
"devDependencies": {
"@custom-elements-manifest/analyzer": "^0.10.2",
"custom-element-vuejs-integration": "^1.2.0",
... generate-vue-types.js import { generateVuejsTypes } from 'custom-element-vuejs-integration';
import manifest from './custom-elements.json' assert { type: 'json' };
const options = {
outdir: './',
fileName: 'vue-types.d.ts',
componentTypePath: (name, tag) => `./src/components/${tag}/index.ts`,
};
generateVuejsTypes(manifest, options); Generated Generated Its not exporting its types, but i think for a starter its better than nothing. There are libs for VSCode and JetBrains custom html and css json generations too. And there is a lib for react component generation, maybe it can help to create a vue component generator? custom-element-react-wrappers |
This issue has been marked as stale because it has required additional |
@benceszenassy I have also been looking at tools and I found https://github.com/open-wc/custom-elements-manifest which I think is the same tool you referece above but a different git repo? And this one https://www.npmjs.com/package/custom-element-jet-brains-integration (I use WebStorm). So I was able to generate this json And from that I generated a VERY simple component for the copy button: <template>
<cds-copy-button v-bind="props">
<slot> Copy to Clipboard </slot>
</cds-copy-button>
</template>
<script setup>
import '@carbon/web-components/es/components/copy-button/index.js';
import { useSlots } from 'vue';
const props = defineProps({
/** Specify an optional className to be added to your Button */
buttonClassName: { type: String },
/** `true` if the button should be disabled. */
disabled: { type: Boolean },
/** Specify the string that is displayed when the button is clicked and the content is copi */
feedback: { type: String, default: 'Copied!' },
/** The number in milliseconds to determine how long the tooltip should remain. */
feedbackTimeout: { type: Number, default: 2000 },
/** Focuses on the first focusable element in the shadow DOM. */
focus: { type: String },
});
</script> The generateReactWrappers is VERY interesting. I failed for some of the dot com components but it worked enough to look promising. I'll look at it this week. I also looked at this one which did not really work at all https://www.npmjs.com/package/@lit-labs/gen-wrapper-vue |
I think the comment above will remove the stale label when the action rus again. |
@davidnixon i thinkered with it too, here is a gist with the generator (its not complete, but it can generate the basics) and with one example of a generated component. https://gist.github.com/benceszenassy/7c06de0043e2dfe937008477489d24eb With emits i dont know what would be the best practice, they didnt get any type for payload... we should create x amount of v-on handler with any as payload type, and simply emit with each? |
This issue has been marked as stale because it has required additional |
@davidnixon I dont have much time to play with the generator, it require more time to build one of these, than convert every component by hand imho. What will be the vision for vue components? It should follow the react components functionality, or the web components? Should built from scracth or use the web components? If it is from web components what should reactivity be like? Or emitted event typing? Im more comfortable with vue from scratch than built a web component wrapper library, but i still want to use this library in work, so if you can tell me the way, i can contribute to it. |
@davidnixon any update on this? |
@benceszenassy Yes, I mostly looked at the JetBrains stuff which was helpful but incomplete. It looks like you found the same. I got stuck on not being able to fully generate a component but being able to generate a skeleton of a component. I think you found the same? Current sticking points for we were the slots. These were not behaving correctly at all for me. I am still most interested in wrapping the web components:
|
@davidnixon i had time on my hands today, so a looked into it - these are examples from the generator i played locally (https://github.com/benceszenassy/cem-tools/tree/main | https://github.com/benceszenassy/carbon-components-vue/tree/carbon11) slotscustom-elements doesn't have scoped slots, so i think its safe to say, that the geneator can handle the slot generation. <template>
<cds-textarea class="cv-textarea" v-bind="props">
<template v-slot:helper-text>
<slot name="helper-text" />
</template>
<template v-slot:label-text>
<slot name="label-text" />
</template>
<template v-slot:validity-message>
<slot name="validity-message" />
</template>
<slot></slot>
</cds-textarea>
</template>
<script setup lang="ts">
import "@carbon/web-components/es/components/textarea/textarea.js";
import type { CvTextareaProps } from "./CvTextarea.ts";
const props = defineProps<CvTextareaProps>();
const emits = defineEmits<{
// undefined
(e: "invalid", value: CustomEvent): void;
}>();
const slots = defineSlots<{
// The helper text.
"helper-text": (scope: any) => any;
// The label text.
"label-text": (scope: any) => any;
// The validity message. If present and non-empty, this input shows the UI of its invalid state.
"validity-message": (scope: any) => any;
}>();
</script> Or we can add scope, just for our own slot handling. <template>
<cds-textarea class="cv-textarea" v-bind="props">
<template #[:helper-text]="scope">
<slot name="helper-text" v-bind="scope" />
</template>
<template #[:label-text]="scope">
<slot name="label-text" v-bind="scope" />
</template>
<template #[:validity-message]="scope">
<slot name="validity-message" v-bind="scope" />
</template>
<slot></slot>
</cds-textarea>
</template>
<script setup lang="ts">
import "@carbon/web-components/es/components/textarea/textarea.js";
import type { CvTextareaProps } from "./CvTextarea.ts";
const props = defineProps<CvTextareaProps>();
const emits = defineEmits<{
// undefined
(e: "invalid", value: CustomEvent): void;
}>();
const slots = defineSlots<{
// The helper text.
"helper-text": (scope: any) => any;
// The label text.
"label-text": (scope: any) => any;
// The validity message. If present and non-empty, this input shows the UI of its invalid state.
"validity-message": (scope: any) => any;
}>();
</script> Or use a single runtime cycle. <template>
<cds-textarea class="cv-textarea" v-bind="props">
<template v-for="(_, slot) of $slots" #[slot]="scope">
<slot :name="slot" v-bind="scope" />
</template>
</cds-textarea>
</template>
<script setup lang="ts">
import "@carbon/web-components/es/components/textarea/textarea.js";
import type { CvTextareaProps } from "./CvTextarea.ts";
const props = defineProps<CvTextareaProps>();
const emits = defineEmits<{
// undefined
(e: "invalid", value: CustomEvent): void;
}>();
const slots = defineSlots<{
// The helper text.
"helper-text": (scope: any) => any;
// The label text.
"label-text": (scope: any) => any;
// The validity message. If present and non-empty, this input shows the UI of its invalid state.
"validity-message": (scope: any) => any;
}>();
</script> emitsFor example the CDSTabs use events from CDSContentSwitcher through a mixin (HostListenerMixin), i think the type data lost on the generic class generation, but if we had the information, in this example, the data that travels with the event is a reference for an HTML element, we can only extract it (blindly without types), and emit it further. // content-switcher.ts
/**
* Handles `click` event on content switcher item.
*
* @param event The event.
* @param event.target The event target.
*/
protected _handleClick({ target }: MouseEvent) {
const currentItem = this._getCurrentItem(target as HTMLElement);
this._handleUserInitiatedSelectItem(currentItem as CDSContentSwitcherItem);
}
/**
* Handles user-initiated selection of a content switcher item.
*
* @param [item] The content switcher item user wants to select.
*/
protected _handleUserInitiatedSelectItem(item: CDSContentSwitcherItem) {
if (!item.disabled && item.value !== this.value) {
const init = {
bubbles: true,
composed: true,
detail: {
item,
},
};
const constructor = this.constructor as typeof CDSContentSwitcher;
const beforeSelectEvent = new CustomEvent(constructor.eventBeforeSelect, {
...init,
cancelable: true,
});
if (this.dispatchEvent(beforeSelectEvent)) {
this._selectionDidChange(item);
const afterSelectEvent = new CustomEvent(constructor.eventSelect, init);
this.dispatchEvent(afterSelectEvent);
}
}
}
/**
* The name of the custom event fired after a a content switcher item is selected upon a user gesture.
*/
static get eventSelect() {
return `${prefix}-content-switcher-selected`;
} // tabs.ts
/**
* The name of the custom event fired after a a tab is selected upon a user gesture.
*/
static get eventSelect() {
return `${prefix}-tabs-selected`;
} <!-- CvTabs.vue -->
<template>
<cds-tabs
class="cv-tabs"
v-bind="props"
@cds-tabs-selected="e => emits('cds-tabs-selected', e.detail.item.value)"
>
<slot></slot>
</cds-tabs>
</template>
<script setup lang="ts">
import '@carbon/web-components/es/components/tabs/tabs';
import type { CvTabsProps } from './CvTabs.ts';
const props = defineProps<CvTabsProps>();
const emits = defineEmits<{
// The custom event fired before a tab is selected upon a user gesture. Cancellation of this event stops changing the user-initiated selection.
(e: 'cds-tabs-beingselected', value: undefined): void;
// The custom event fired after a a tab is selected upon a user gesture.
(e: 'cds-tabs-selected', value: undefined): void;
// The custom event fired before a content switcher item is selected upon a user gesture. Cancellation of this event stops changing the user-initiated selection.
(e: 'cds-content-switcher-beingselected', value: undefined): void;
// The custom event fired after a a content switcher item is selected upon a user gesture.
(e: 'cds-content-switcher-selected', value: undefined): void;
}>();
</script> v-modelWith the previous example we can only define / use v-model by-hand. <template>
<cds-tabs class="cv-tabs" v-bind="props" @cds-tabs-selected="onSelect">
<slot></slot>
</cds-tabs>
</template>
<script setup lang="ts">
import '@carbon/web-components/es/components/tabs/tabs';
import type { CvTabsProps } from './CvTabs.ts';
const props = defineProps<CvTabsProps>();
const model = defineModel('value');
const onSelect = e => {
emits('cds-tabs-selected', e.detail.item.value);
model.value = e.detail.item.value;
};
const emits = defineEmits<{
// The custom event fired before a tab is selected upon a user gesture. Cancellation of this event stops changing the user-initiated selection.
(e: 'cds-tabs-beingselected', value: undefined): void;
// The custom event fired after a a tab is selected upon a user gesture.
(e: 'cds-tabs-selected', value: undefined): void;
// The custom event fired before a content switcher item is selected upon a user gesture. Cancellation of this event stops changing the user-initiated selection.
(e: 'cds-content-switcher-beingselected', value: undefined): void;
// The custom event fired after a a content switcher item is selected upon a user gesture.
(e: 'cds-content-switcher-selected', value: undefined): void;
}>();
</script> propsI think it depends on the custom elements not the vue component it self. |
@davidnixon let me know if i can help with anything else on this topic |
Is your feature request related to a problem? Please describe.
We should keep up, with the design kit, react and angular components.
Describe the solution you'd like
On a separate branch, we should update the components incrementally, based on the react components.
Additional context
There are lot of basic features missing from v10 like readonly form fields, toasts that remove them selves after x ms, etc.
The text was updated successfully, but these errors were encountered: