fix: shrink custom element baseline a bit (#8858)

- use shorter class property names as they are not minified
- reuse some dom helper methods

#8826
pull/8898/head
Simon H 1 year ago committed by GitHub
parent 2f5e371046
commit 4b3eb72346
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: shrink custom element baseline a bit

@ -13,7 +13,9 @@ import {
start_hydrating, start_hydrating,
end_hydrating, end_hydrating,
get_custom_elements_slots, get_custom_elements_slots,
insert insert,
element,
attr
} from './dom.js'; } from './dom.js';
import { transition_in } from './transitions.js'; import { transition_in } from './transitions.js';
@ -157,23 +159,29 @@ export let SvelteElement;
if (typeof HTMLElement === 'function') { if (typeof HTMLElement === 'function') {
SvelteElement = class extends HTMLElement { SvelteElement = class extends HTMLElement {
$$componentCtor; /** The Svelte component constructor */
$$slots; $$ctor;
$$component; /** Slots */
$$connected = false; $$s;
$$data = {}; /** The Svelte component instance */
$$reflecting = false; $$c;
/** @type {Record<string, CustomElementPropDefinition>} */ /** Whether or not the custom element is connected */
$$props_definition = {}; $$cn = false;
/** @type {Record<string, Function[]>} */ /** Component props data */
$$listeners = {}; $$d = {};
/** @type {Map<Function, Function>} */ /** `true` if currently in the process of reflecting component props back to attributes */
$$listener_unsubscribe_fns = new Map(); $$r = false;
/** @type {Record<string, CustomElementPropDefinition>} Props definition (name, reflected, type etc) */
$$p_d = {};
/** @type {Record<string, Function[]>} Event listeners */
$$l = {};
/** @type {Map<Function, Function>} Event listener unsubscribe functions */
$$l_u = new Map();
constructor($$componentCtor, $$slots, use_shadow_dom) { constructor($$componentCtor, $$slots, use_shadow_dom) {
super(); super();
this.$$componentCtor = $$componentCtor; this.$$ctor = $$componentCtor;
this.$$slots = $$slots; this.$$s = $$slots;
if (use_shadow_dom) { if (use_shadow_dom) {
this.attachShadow({ mode: 'open' }); this.attachShadow({ mode: 'open' });
} }
@ -183,32 +191,32 @@ if (typeof HTMLElement === 'function') {
// We can't determine upfront if the event is a custom event or not, so we have to // We can't determine upfront if the event is a custom event or not, so we have to
// listen to both. If someone uses a custom event with the same name as a regular // listen to both. If someone uses a custom event with the same name as a regular
// browser event, this fires twice - we can't avoid that. // browser event, this fires twice - we can't avoid that.
this.$$listeners[type] = this.$$listeners[type] || []; this.$$l[type] = this.$$l[type] || [];
this.$$listeners[type].push(listener); this.$$l[type].push(listener);
if (this.$$component) { if (this.$$c) {
const unsub = this.$$component.$on(type, listener); const unsub = this.$$c.$on(type, listener);
this.$$listener_unsubscribe_fns.set(listener, unsub); this.$$l_u.set(listener, unsub);
} }
super.addEventListener(type, listener, options); super.addEventListener(type, listener, options);
} }
removeEventListener(type, listener, options) { removeEventListener(type, listener, options) {
super.removeEventListener(type, listener, options); super.removeEventListener(type, listener, options);
if (this.$$component) { if (this.$$c) {
const unsub = this.$$listener_unsubscribe_fns.get(listener); const unsub = this.$$l_u.get(listener);
if (unsub) { if (unsub) {
unsub(); unsub();
this.$$listener_unsubscribe_fns.delete(listener); this.$$l_u.delete(listener);
} }
} }
} }
async connectedCallback() { async connectedCallback() {
this.$$connected = true; this.$$cn = true;
if (!this.$$component) { if (!this.$$c) {
// We wait one tick to let possible child slot elements be created/mounted // We wait one tick to let possible child slot elements be created/mounted
await Promise.resolve(); await Promise.resolve();
if (!this.$$connected) { if (!this.$$cn) {
return; return;
} }
function create_slot(name) { function create_slot(name) {
@ -216,9 +224,9 @@ if (typeof HTMLElement === 'function') {
let node; let node;
const obj = { const obj = {
c: function create() { c: function create() {
node = document.createElement('slot'); node = element('slot');
if (name !== 'default') { if (name !== 'default') {
node.setAttribute('name', name); attr(node, 'name', name);
} }
}, },
/** /**
@ -239,74 +247,64 @@ if (typeof HTMLElement === 'function') {
} }
const $$slots = {}; const $$slots = {};
const existing_slots = get_custom_elements_slots(this); const existing_slots = get_custom_elements_slots(this);
for (const name of this.$$slots) { for (const name of this.$$s) {
if (name in existing_slots) { if (name in existing_slots) {
$$slots[name] = [create_slot(name)]; $$slots[name] = [create_slot(name)];
} }
} }
for (const attribute of this.attributes) { for (const attribute of this.attributes) {
// this.$$data takes precedence over this.attributes // this.$$data takes precedence over this.attributes
const name = this.$$get_prop_name(attribute.name); const name = this.$$g_p(attribute.name);
if (!(name in this.$$data)) { if (!(name in this.$$d)) {
this.$$data[name] = get_custom_element_value( this.$$d[name] = get_custom_element_value(name, attribute.value, this.$$p_d, 'toProp');
name,
attribute.value,
this.$$props_definition,
'toProp'
);
} }
} }
this.$$component = new this.$$componentCtor({ this.$$c = new this.$$ctor({
target: this.shadowRoot || this, target: this.shadowRoot || this,
props: { props: {
...this.$$data, ...this.$$d,
$$slots, $$slots,
$$scope: { $$scope: {
ctx: [] ctx: []
} }
} }
}); });
for (const type in this.$$listeners) { for (const type in this.$$l) {
for (const listener of this.$$listeners[type]) { for (const listener of this.$$l[type]) {
const unsub = this.$$component.$on(type, listener); const unsub = this.$$c.$on(type, listener);
this.$$listener_unsubscribe_fns.set(listener, unsub); this.$$l_u.set(listener, unsub);
} }
} }
this.$$listeners = {}; this.$$l = {};
} }
} }
// We don't need this when working within Svelte code, but for compatibility of people using this outside of Svelte // We don't need this when working within Svelte code, but for compatibility of people using this outside of Svelte
// and setting attributes through setAttribute etc, this is helpful // and setting attributes through setAttribute etc, this is helpful
attributeChangedCallback(attr, _oldValue, newValue) { attributeChangedCallback(attr, _oldValue, newValue) {
if (this.$$reflecting) return; if (this.$$r) return;
attr = this.$$get_prop_name(attr); attr = this.$$g_p(attr);
this.$$data[attr] = get_custom_element_value( this.$$d[attr] = get_custom_element_value(attr, newValue, this.$$p_d, 'toProp');
attr, this.$$c?.$set({ [attr]: this.$$d[attr] });
newValue,
this.$$props_definition,
'toProp'
);
this.$$component?.$set({ [attr]: this.$$data[attr] });
} }
disconnectedCallback() { disconnectedCallback() {
this.$$connected = false; this.$$cn = false;
// In a microtask, because this could be a move within the DOM // In a microtask, because this could be a move within the DOM
Promise.resolve().then(() => { Promise.resolve().then(() => {
if (!this.$$connected) { if (!this.$$cn) {
this.$$component.$destroy(); this.$$c.$destroy();
this.$$component = undefined; this.$$c = undefined;
} }
}); });
} }
$$get_prop_name(attribute_name) { $$g_p(attribute_name) {
return ( return (
Object.keys(this.$$props_definition).find( Object.keys(this.$$p_d).find(
(key) => (key) =>
this.$$props_definition[key].attribute === attribute_name || this.$$p_d[key].attribute === attribute_name ||
(!this.$$props_definition[key].attribute && key.toLowerCase() === attribute_name) (!this.$$p_d[key].attribute && key.toLowerCase() === attribute_name)
) || attribute_name ) || attribute_name
); );
} }
@ -371,7 +369,7 @@ export function create_custom_element(
const Class = class extends SvelteElement { const Class = class extends SvelteElement {
constructor() { constructor() {
super(Component, slots, use_shadow_dom); super(Component, slots, use_shadow_dom);
this.$$props_definition = props_definition; this.$$p_d = props_definition;
} }
static get observedAttributes() { static get observedAttributes() {
return Object.keys(props_definition).map((key) => return Object.keys(props_definition).map((key) =>
@ -382,16 +380,14 @@ export function create_custom_element(
Object.keys(props_definition).forEach((prop) => { Object.keys(props_definition).forEach((prop) => {
Object.defineProperty(Class.prototype, prop, { Object.defineProperty(Class.prototype, prop, {
get() { get() {
return this.$$component && prop in this.$$component return this.$$c && prop in this.$$c ? this.$$c[prop] : this.$$d[prop];
? this.$$component[prop]
: this.$$data[prop];
}, },
set(value) { set(value) {
value = get_custom_element_value(prop, value, props_definition); value = get_custom_element_value(prop, value, props_definition);
this.$$data[prop] = value; this.$$d[prop] = value;
this.$$component?.$set({ [prop]: value }); this.$$c?.$set({ [prop]: value });
if (props_definition[prop].reflect) { if (props_definition[prop].reflect) {
this.$$reflecting = true; this.$$r = true;
const attribute_value = get_custom_element_value( const attribute_value = get_custom_element_value(
prop, prop,
value, value,
@ -403,7 +399,7 @@ export function create_custom_element(
} else { } else {
this.setAttribute(props_definition[prop].attribute || prop, attribute_value); this.setAttribute(props_definition[prop].attribute || prop, attribute_value);
} }
this.$$reflecting = false; this.$$r = false;
} }
} }
}); });
@ -411,7 +407,7 @@ export function create_custom_element(
accessors.forEach((accessor) => { accessors.forEach((accessor) => {
Object.defineProperty(Class.prototype, accessor, { Object.defineProperty(Class.prototype, accessor, {
get() { get() {
return this.$$component?.[accessor]; return this.$$c?.[accessor];
} }
}); });
}); });

Loading…
Cancel
Save