|
|
|
@ -1,9 +1,10 @@
|
|
|
|
import { add_render_callback, flush, flush_render_callbacks, schedule_update, dirty_components } from './scheduler';
|
|
|
|
import { add_render_callback, flush, flush_render_callbacks, schedule_update, dirty_components, tick } from './scheduler';
|
|
|
|
import { current_component, set_current_component } from './lifecycle';
|
|
|
|
import { current_component, set_current_component } from './lifecycle';
|
|
|
|
import { blank_object, is_empty, is_function, run, run_all, noop } from './utils';
|
|
|
|
import { blank_object, is_empty, is_function, run, run_all, noop } from './utils';
|
|
|
|
import { children, detach, start_hydrating, end_hydrating } from './dom';
|
|
|
|
import { children, detach, start_hydrating, end_hydrating, set_custom_element_data, get_custom_elements_slots, insert } from './dom';
|
|
|
|
import { transition_in } from './transitions';
|
|
|
|
import { transition_in } from './transitions';
|
|
|
|
import { T$$ } from './types';
|
|
|
|
import { T$$ } from './types';
|
|
|
|
|
|
|
|
import { ComponentType } from './dev';
|
|
|
|
|
|
|
|
|
|
|
|
export function bind(component, name, callback) {
|
|
|
|
export function bind(component, name, callback) {
|
|
|
|
const index = component.$$.props[name];
|
|
|
|
const index = component.$$.props[name];
|
|
|
|
@ -21,12 +22,11 @@ export function claim_component(block, parent_nodes) {
|
|
|
|
block && block.l(parent_nodes);
|
|
|
|
block && block.l(parent_nodes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function mount_component(component, target, anchor, customElement) {
|
|
|
|
export function mount_component(component, target, anchor) {
|
|
|
|
const { fragment, after_update } = component.$$;
|
|
|
|
const { fragment, after_update } = component.$$;
|
|
|
|
|
|
|
|
|
|
|
|
fragment && fragment.m(target, anchor);
|
|
|
|
fragment && fragment.m(target, anchor);
|
|
|
|
|
|
|
|
|
|
|
|
if (!customElement) {
|
|
|
|
|
|
|
|
// onMount happens before the initial afterUpdate
|
|
|
|
// onMount happens before the initial afterUpdate
|
|
|
|
add_render_callback(() => {
|
|
|
|
add_render_callback(() => {
|
|
|
|
|
|
|
|
|
|
|
|
@ -43,7 +43,6 @@ export function mount_component(component, target, anchor, customElement) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
component.$$.on_mount = [];
|
|
|
|
component.$$.on_mount = [];
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
after_update.forEach(add_render_callback);
|
|
|
|
after_update.forEach(add_render_callback);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -137,7 +136,7 @@ export function init(component, options, instance, create_fragment, not_equal, p
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (options.intro) transition_in(component.$$.fragment);
|
|
|
|
if (options.intro) transition_in(component.$$.fragment);
|
|
|
|
mount_component(component, options.target, options.anchor, options.customElement);
|
|
|
|
mount_component(component, options.target, options.anchor);
|
|
|
|
end_hydrating();
|
|
|
|
end_hydrating();
|
|
|
|
flush();
|
|
|
|
flush();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -148,59 +147,202 @@ export function init(component, options, instance, create_fragment, not_equal, p
|
|
|
|
export let SvelteElement;
|
|
|
|
export let SvelteElement;
|
|
|
|
if (typeof HTMLElement === 'function') {
|
|
|
|
if (typeof HTMLElement === 'function') {
|
|
|
|
SvelteElement = class extends HTMLElement {
|
|
|
|
SvelteElement = class extends HTMLElement {
|
|
|
|
$$: T$$;
|
|
|
|
private $$component?: SvelteComponent;
|
|
|
|
$$set?: ($$props: any) => void;
|
|
|
|
private $$connected = false;
|
|
|
|
constructor() {
|
|
|
|
private $$data = {};
|
|
|
|
|
|
|
|
private $$reflecting = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
|
|
|
|
private $$componentCtor: ComponentType,
|
|
|
|
|
|
|
|
private $$slots: string[],
|
|
|
|
|
|
|
|
) {
|
|
|
|
super();
|
|
|
|
super();
|
|
|
|
this.attachShadow({ mode: 'open' });
|
|
|
|
this.attachShadow({ mode: 'open' });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
addEventListener(type: string, listener: any, options?: any): void {
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
|
|
// browser event, this fires twice - we can't avoid that.
|
|
|
|
|
|
|
|
this.$$component!.$on(type, listener);
|
|
|
|
|
|
|
|
super.addEventListener(type, listener, options);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
connectedCallback() {
|
|
|
|
connectedCallback() {
|
|
|
|
const { on_mount } = this.$$;
|
|
|
|
this.$$connected = true;
|
|
|
|
this.$$.on_disconnect = on_mount.map(run).filter(is_function);
|
|
|
|
if (!this.$$component) {
|
|
|
|
|
|
|
|
for (const attribute of this.attributes) {
|
|
|
|
|
|
|
|
// this.$$data takes precedence over this.attributes
|
|
|
|
|
|
|
|
if (!(attribute.name in this.$$data)) {
|
|
|
|
|
|
|
|
this.$$data[attribute.name] = attribute.value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// @ts-ignore todo: improve typings
|
|
|
|
function create_slot(name: string) {
|
|
|
|
for (const key in this.$$.slotted) {
|
|
|
|
return () => {
|
|
|
|
// @ts-ignore todo: improve typings
|
|
|
|
let node: HTMLSlotElement;
|
|
|
|
this.appendChild(this.$$.slotted[key]);
|
|
|
|
return {
|
|
|
|
|
|
|
|
c: function create() {
|
|
|
|
|
|
|
|
node = document.createElement('slot');
|
|
|
|
|
|
|
|
if (name !== 'default') {
|
|
|
|
|
|
|
|
node.setAttribute('name', name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
m: function mount(target: HTMLElement, anchor?: HTMLElement) {
|
|
|
|
|
|
|
|
insert(target, node, anchor);
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
d: function destroy(detaching: boolean) {
|
|
|
|
|
|
|
|
if (detaching) {
|
|
|
|
|
|
|
|
detach(node)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
$$c_e: true
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
attributeChangedCallback(attr, _oldValue, newValue) {
|
|
|
|
let $$slots: Record<string, any> = {};
|
|
|
|
this[attr] = newValue;
|
|
|
|
const existing_slots = get_custom_elements_slots(this);
|
|
|
|
|
|
|
|
for (const name of this.$$slots) {
|
|
|
|
|
|
|
|
if (name in existing_slots) {
|
|
|
|
|
|
|
|
$$slots[name] = [create_slot(name)];
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
disconnectedCallback() {
|
|
|
|
// Dilemma: We need to set the component props eagerly or they have the wrong value for actions/onMount etc.
|
|
|
|
run_all(this.$$.on_disconnect);
|
|
|
|
// Boolean attributes are represented by the empty string, and we don't know if they represent boolean or string props.
|
|
|
|
|
|
|
|
this.$$component = new this.$$componentCtor({
|
|
|
|
|
|
|
|
target: this.shadowRoot!,
|
|
|
|
|
|
|
|
props: {
|
|
|
|
|
|
|
|
$$slots,
|
|
|
|
|
|
|
|
$$scope: {
|
|
|
|
|
|
|
|
ctx: []
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
// ensures that <foo-bar boolean-attribute /> works correctly
|
|
|
|
|
|
|
|
Object.keys(this.$$data).forEach(key => {
|
|
|
|
|
|
|
|
set_custom_element_data(this, key, this.$$data[key]);
|
|
|
|
|
|
|
|
this.$$data[key] = this[key]; // "" -> true for boolean attributes
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$destroy() {
|
|
|
|
// TODO we don't need this when working within Svelte code, but for compatibility of people using this outside of Svelte
|
|
|
|
destroy_component(this, 1);
|
|
|
|
// and setting attributes through setAttribute etc, this is probably helpful
|
|
|
|
this.$destroy = noop;
|
|
|
|
attributeChangedCallback(attr: string, _oldValue: any, newValue: any) {
|
|
|
|
|
|
|
|
if (this.$$reflecting) return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
set_custom_element_data(this.$$data, attr, newValue);
|
|
|
|
|
|
|
|
this.$$component![attr] = this.$$data;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$on(type, callback) {
|
|
|
|
disconnectedCallback() {
|
|
|
|
// TODO should this delegate to addEventListener?
|
|
|
|
this.$$connected = false;
|
|
|
|
if (!is_function(callback)) {
|
|
|
|
// In a microtask, because this could be a move within the DOM
|
|
|
|
return noop;
|
|
|
|
tick().then(() => {
|
|
|
|
|
|
|
|
if (!this.$$connected) {
|
|
|
|
|
|
|
|
this.$$component!.$destroy();
|
|
|
|
|
|
|
|
this.$$component = undefined;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = []));
|
|
|
|
});
|
|
|
|
callbacks.push(callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
/**
|
|
|
|
const index = callbacks.indexOf(callback);
|
|
|
|
* Attribute value types that should be reflected to the DOM. Helpful
|
|
|
|
if (index !== -1) callbacks.splice(index, 1);
|
|
|
|
* for people relying on the custom element's attributes to be present,
|
|
|
|
|
|
|
|
* for example when using a CSS selector which relies on an attribute.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
const should_reflect = ['string', 'number', 'boolean'];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function camelToHyphen(str: string) {
|
|
|
|
|
|
|
|
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Turn a Svelte component into a custom element.
|
|
|
|
|
|
|
|
* @param Component A Svelte component constructor
|
|
|
|
|
|
|
|
* @param props The props to observe
|
|
|
|
|
|
|
|
* @param slots The slots to create
|
|
|
|
|
|
|
|
* @param accessors Other accessors besides the ones for props the component has
|
|
|
|
|
|
|
|
* @param styles Additional styles to apply to the shadow root (not needed for Svelte components compiled with `customElement: true`)
|
|
|
|
|
|
|
|
* @returns A custom element class
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
export function create_custom_element(
|
|
|
|
|
|
|
|
Component: ComponentType,
|
|
|
|
|
|
|
|
props: string[],
|
|
|
|
|
|
|
|
slots: string[],
|
|
|
|
|
|
|
|
accessors: string[],
|
|
|
|
|
|
|
|
styles?: string,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
const Class = class extends SvelteElement {
|
|
|
|
|
|
|
|
constructor() {
|
|
|
|
|
|
|
|
super(Component, slots);
|
|
|
|
|
|
|
|
if (styles) {
|
|
|
|
|
|
|
|
const style = document.createElement('style');
|
|
|
|
|
|
|
|
style.textContent = styles;
|
|
|
|
|
|
|
|
this.shadowRoot!.appendChild(style);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static get observedAttributes() {
|
|
|
|
|
|
|
|
return props;
|
|
|
|
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function createProperty(name: string, prop: string) {
|
|
|
|
|
|
|
|
Object.defineProperty(Class.prototype, name, {
|
|
|
|
|
|
|
|
get() {
|
|
|
|
|
|
|
|
return this.$$component && prop in this.$$component
|
|
|
|
|
|
|
|
? this.$$component[prop]
|
|
|
|
|
|
|
|
: this.$$data[prop];
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
set(value) {
|
|
|
|
|
|
|
|
this.$$data[prop] = value;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (this.$$component) {
|
|
|
|
|
|
|
|
if(should_reflect.indexOf(typeof value) !== -1) {
|
|
|
|
|
|
|
|
this.$$reflecting = true;
|
|
|
|
|
|
|
|
if (value === false || value == null) {
|
|
|
|
|
|
|
|
this.removeAttribute(prop);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
this.setAttribute(prop, value);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
this.$$reflecting = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$set($$props) {
|
|
|
|
this.$$component[prop] = value;
|
|
|
|
if (this.$$set && !is_empty($$props)) {
|
|
|
|
|
|
|
|
this.$$.skip_bound = true;
|
|
|
|
|
|
|
|
this.$$set($$props);
|
|
|
|
|
|
|
|
this.$$.skip_bound = false;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
props.forEach((prop) => {
|
|
|
|
|
|
|
|
createProperty(prop, prop);
|
|
|
|
|
|
|
|
// <c-e camelCase="foo" /> will be ce.camcelcase = "foo"
|
|
|
|
|
|
|
|
const lower = prop.toLowerCase();
|
|
|
|
|
|
|
|
if (lower !== prop) {
|
|
|
|
|
|
|
|
createProperty(lower, prop);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// also support hyphenated version where <c-e camel-case="foo" /> will be ce['camel-case'] = "foo"
|
|
|
|
|
|
|
|
const hyphen = camelToHyphen(prop);
|
|
|
|
|
|
|
|
if (hyphen !== lower) {
|
|
|
|
|
|
|
|
createProperty(hyphen, prop)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
accessors.forEach(accessor => {
|
|
|
|
|
|
|
|
Object.defineProperty(Class.prototype, accessor, {
|
|
|
|
|
|
|
|
get() {
|
|
|
|
|
|
|
|
return this.$$component?.[accessor];
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return Class;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
|