From 6b5b0c93414fe67e2f1dd4c1fe740fd793d94c9c Mon Sep 17 00:00:00 2001 From: MMMJB Date: Wed, 24 Jul 2024 16:30:56 -0400 Subject: [PATCH 1/4] build: set up umd bundling --- demo/public/index.html | 16 - demo/public/scripts/bundle.js | 737 ++++++++++++++++++++++++++++++ demo/public/scripts/bundle.js.map | 1 + demo/public/static-demo.html | 32 ++ demo/src/index.css | 65 +-- package-lock.json | 389 +++++++++++++--- package.json | 3 + rollup.config.js | 32 ++ scripts/build.sh | 12 +- 9 files changed, 1139 insertions(+), 148 deletions(-) create mode 100644 demo/public/scripts/bundle.js create mode 100644 demo/public/scripts/bundle.js.map create mode 100644 demo/public/static-demo.html create mode 100644 rollup.config.js diff --git a/demo/public/index.html b/demo/public/index.html index a850053..135732d 100644 --- a/demo/public/index.html +++ b/demo/public/index.html @@ -8,23 +8,7 @@ - - -
diff --git a/demo/public/scripts/bundle.js b/demo/public/scripts/bundle.js new file mode 100644 index 0000000..ad3ffff --- /dev/null +++ b/demo/public/scripts/bundle.js @@ -0,0 +1,737 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react/jsx-runtime'), require('react'), require('react-dom/client')) : + typeof define === 'function' && define.amd ? define(['exports', 'react/jsx-runtime', 'react', 'react-dom/client'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.launched = {}, global.jsxRuntime, global.React, global.ReactDOM)); +})(this, (function (exports, jsxRuntime, react, client) { 'use strict'; + + class EventEmitter { + events; + constructor() { + this.events = {}; + } + on(event, listener) { + if (!this.events[event]) { + this.events[event] = []; + } + this.events[event].push(listener); + } + off(event, listener) { + if (!this.events[event]) + return; + this.events[event] = this.events[event].filter((l) => l !== listener); + } + emit(event, ...args) { + if (!this.events[event]) + return; + this.events[event].forEach((listener) => listener(...args)); + } + } + // Events: + // tag:ready: a tag is bound to an element => id, tag + // tag:mount: tag UI component renders => id, tag + // tag:unmount: tag UI component unmounts => id, tag + // tag:select: tag UI gains focus => id, tag + // tag:deselect: tag UI loses focus => id, tag + // tag:change: a single tag is updated => key, newValue, originalValue + // data:update: tag data changes => newTagData + // data:lock: tag data is locked + // data:unlock: tag data is unlocked + + function error(msg) { + throw new Error(`Launched error: ${msg}`); + } + + function flattenTagValue(value) { + if (Array.isArray(value)) + return value.map((v) => flattenTagValue(v)); + else if (typeof value === "object") { + return Object.fromEntries(Object.entries(value).map(([key, v]) => { + if (typeof v === "object" && "value" in v) + return [key, flattenTagValue(v.value)]; + else + return [key, flattenTagValue(v)]; + })); + } + else + return value; + } + + class Renderer { + static formats = new Map(); + static roots = new Map(); + initialRenderOptions = new Map(); + constructor() { } + static registerTagFormat(name, renderer) { + if (!renderer.component) + error("Custom renderers must have a component."); + Renderer.formats.set(name, renderer); + } + renderSingleTagUI(parentTag, id, options, dry) { + if (!parentTag || !parentTag.el.current) + return console.warn(`Tag "${id}" was never bound to an element.`); + const renderTag = (parentTag, tag, childId, index) => { + if (!tag.el.current) + return; + if (Array.isArray(tag.data.value)) { + tag.data.value.forEach((t, i) => { + const childEl = tag.el.current.children[i] ?? tag.el.current; + renderTag(parentTag, { + el: { current: childEl }, + data: { + type: tag.data.type, + value: t, + }, + setData: (data) => { + tag.setData(tag.data.value.map((v, index) => index === i + ? (typeof data === "function" + ? data(v) + : data) + : v)); + }, + }, `${id}-${i}`, i); + }); + } + else if (tag.data.type === "object" && + typeof tag.data.value === "object") { + for (const key in tag.data.value) { + const childEl = tag.el.current.querySelector(`[data-key="${key}"]`); + if (!childEl) + error(`Child element with key "${key}" (under "${id}") not found. If you're using a custom renderer, make sure to add a data-key attribute to the targeted element.`); + renderTag(parentTag, { + el: { current: childEl }, + data: { + type: tag.data.value[key].type, + value: tag.data.value[key].value, + }, + setData: (data) => { + const newValue = typeof data === "function" + ? data(tag.data.value[key].value) + : data; + tag.setData({ + ...tag.data.value, + [key]: { + type: tag.data.value[key] + .type, + value: newValue, + }, + }); + }, + }, `${childId}-${key}`, index); + } + } + else { + if (!tag.el.current) + return; + const renderer = Renderer.formats.get(tag.data.type); + if (!renderer) { + return console.warn(`No renderer found for tag type "${tag.data.type}".`); + } + if (renderer.parentValidator && + !renderer.parentValidator(tag.el.current)) { + return console.warn(`Parent element of tag "${childId}" does not satisfy the constraints of the "${tag.data.type}" renderer.`); + } + const id = `Lt-${childId.replaceAll(" ", "-")}`; + let userOptions; + if (this.initialRenderOptions.get(id)) + userOptions = this.initialRenderOptions.get(id); + else { + userOptions = { + arrayMutable: options?.arrayMutable ?? false, + index, + parentTag, + }; + this.initialRenderOptions.set(id, userOptions); + } + if (dry) + return; + const existingNode = document.getElementById(id); + if (existingNode) + existingNode.remove(); + setTimeout(() => { + if (Renderer.roots.get(childId)) { + Renderer.roots.get(childId).unmount(); + Renderer.roots.delete(childId); + } + const rootNode = document.createElement("div"); + rootNode.id = id; + tag.el.current.appendChild(rootNode); + const root = client.createRoot(rootNode); + Renderer.roots.set(childId, root); + const t = { + ...tag, + data: { + type: tag.data.type, + value: flattenTagValue(tag.data.value), + }, + }; + root.render(jsxRuntime.jsx(TagUI, { tag: t, renderer: renderer, id: childId, options: userOptions })); + }, 0); + } + }; + renderTag(parentTag, parentTag, id, 0); + } + unmountSingleTagUI(tagId) { + const id = `Lt-${tagId.split(" ").join("-")}`; + const root = Renderer.roots.get(tagId); + if (root) { + root.unmount(); + Renderer.roots.delete(tagId); + } + const node = document.getElementById(id); + if (node) + node.remove(); + } + } + function TagUI({ tag, renderer, id, options, }) { + const containerRef = react.useRef(null); + const [selected, setSelected] = react.useState(false); + const { arrayMutable, parentTag, index } = options; + function close() { + setSelected(false); + containerRef.current?.blur(); + renderer.onClose?.({ + element: tag.el.current ?? undefined, + }); + Launched.events.emit("tag:deselect", id, tag); + } + function updateData(data) { + tag.setData(data); + renderer.onDataUpdate?.({ + element: tag.el.current ?? undefined, + data, + }); + // @ts-expect-error + tag.el.current = null; + } + function duplicateTagItem() { + if (!Array.isArray(parentTag.data.value)) + return; + parentTag.setData((p) => [ + ...p.slice(0, index + 1), + p[index], + ...p.slice(index + 1), + ]); + } + function removeTagItem() { + if (!Array.isArray(parentTag.data.value)) + return; + parentTag.setData((p) => [ + ...p.slice(0, index), + ...p.slice(index + 1), + ]); + } + function onTagSelect(selectedId) { + if (selectedId !== id) + setSelected(false); + } + react.useEffect(() => { + if (!tag.el.current) + error("Element is null."); + tag.el.current.classList.add("tagged"); + if (getComputedStyle(tag.el.current).position === "static") { + tag.el.current.style.position = "relative"; + } + Launched.events.emit("tag:mount", id, tag); + Launched.events.on("tag:select", onTagSelect); + return () => { + Launched.events.emit("tag:unmount", id, tag); + Launched.events.off("tag:select", onTagSelect); + }; + }, []); + function select() { + if (selected) + return; + setSelected(true); + renderer?.onSelect?.({ element: tag.el.current }); + Launched.events.emit("tag:select", id, tag); + } + if (!tag.el.current) + return null; + return (jsxRuntime.jsxs("div", { ref: containerRef, tabIndex: 0, onMouseDown: select, onFocus: select, className: `Launched__tag-container ${selected && "active"}`, children: [jsxRuntime.jsx(renderer.component, { element: tag.el.current, value: tag.data.value, selected: selected, updateData: (v) => updateData(v), close: () => close(), id: id, context: Launched.instance }), arrayMutable && (jsxRuntime.jsxs("div", { onMouseDown: (e) => e.preventDefault(), className: "Launched__tag-arrayControls Launched__toolbar-tools", children: [jsxRuntime.jsx("button", { className: "Launched__toolbar-button add", onClick: duplicateTagItem, children: jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", className: "Launched__icon", children: [jsxRuntime.jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }), jsxRuntime.jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })] }) }), jsxRuntime.jsx("button", { className: "Launched__button remove", onClick: removeTagItem, children: jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", className: "Launched__icon", children: [jsxRuntime.jsx("polyline", { points: "3 6 5 6 21 6" }), jsxRuntime.jsx("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })] }) })] }))] })); + } + + function Toolbar({ position, className, canUndo, canRedo, undo, redo, save, revert, }) { + // @ts-expect-error + const [disabled, setDisabled] = react.useState(Launched.instance.config.locked); + return (jsxRuntime.jsx("div", { "data-position": position, className: `Launched__toolbar ${className || ""} ${disabled && "disabled"}`, children: jsxRuntime.jsxs("div", { className: "Launched__toolbar-tools", children: [jsxRuntime.jsx("button", { onClick: save, className: "Launched__toolbar-saveButton Launched__button", children: "Save" }), jsxRuntime.jsxs("select", { onChange: (e) => { + if (e.target.value === "locked") { + Launched.lock(); + setDisabled(true); + } + else { + Launched.unlock(); + setDisabled(false); + } + }, className: "Launched__toolbar-lockMode", value: disabled ? "locked" : "unlocked", children: [jsxRuntime.jsx("option", { value: "unlocked", children: "Edit" }), jsxRuntime.jsx("option", { value: "locked", children: "Preview" })] }), jsxRuntime.jsx("button", { disabled: !canUndo, onClick: undo, className: "Launched__toolbar-button undo", children: jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", className: "Launched__icon", children: [jsxRuntime.jsx("polyline", { points: "1 4 1 10 7 10" }), jsxRuntime.jsx("path", { d: "M3.51 15a9 9 0 1 0 2.13-9.36L1 10" })] }) }), jsxRuntime.jsx("button", { disabled: !canRedo, onClick: redo, className: "Launched__toolbar-button redo", children: jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", className: "Launched__icon", children: [jsxRuntime.jsx("polyline", { points: "23 4 23 10 17 10" }), jsxRuntime.jsx("path", { d: "M20.49 15a9 9 0 1 1-2.12-9.36L23 10" })] }) }), jsxRuntime.jsx("button", { onClick: revert, className: "Launched__toolbar-revertButton Launched__button", children: "Revert" })] }) })); + } + + function validateObject(tag) { + if ("type" in tag && "value" in tag) + return; + if (Object.values(tag).some((v) => typeof v === "object" && !("type" in v) && !("value" in v))) { + error("Objects cannot have nested objects without an explicit type."); + } + } + function transformObjectToTagData(value, type) { + if (typeof value === "object" && "type" in value && "value" in value) + return value; + return Object.fromEntries(Object.entries(value).map(([k, v]) => { + if (typeof v !== "object") + return [k, { type: typeof v, value: v }]; + else + return [k, transformObjectToTagData(v)]; + })); + } + function transformTag(tag, type) { + if (Array.isArray(tag)) { + if (!tag.length) + return { type, value: [] }; + else if (tag.some((v) => typeof v !== typeof tag[0])) + error("Array must have items of the same type."); + if (typeof tag[0] === "object") { + if (tag.some((v) => Array.isArray(v))) + error("Array cannot have nested arrays."); + const keys = tag.map((v) => Object.keys(v)); + if (keys[0].some((key) => keys.some((k) => !k.includes(key)))) + error("Objects must have the same keys."); + validateObject(tag[0]); + return { + type, + value: tag.map((v) => transformObjectToTagData(v)), + }; + } + else + return { + type, + value: tag, + }; + } + else if (typeof tag === "object") { + validateObject(tag); + const value = transformObjectToTagData(tag); + return { + type, + value, + }; + } + else { + return { + type, + value: tag, + }; + } + } + function createTag(tag, type) { + const t = transformTag(tag, type); + return { + el: react.createRef(), + data: t, + }; + } + + function tagToValues(tag) { + if (Array.isArray(tag.data.value)) { + return tag.data.value.map((t) => tagToValues({ + ...tag, + data: { + ...tag.data, + value: t, + }, + })); + } + else if (typeof tag.data.value === "object") { + // @ts-expect-error + return flattenTagValue(tag.data.value); + } + else + return tag.data.value; + } + + function mergeDeep(target, ...sources) { + const isObject = (t) => t && typeof t === "object" && !Array.isArray(t); + if (!sources.length) + return target; + const source = sources.shift(); + if (isObject(target) && isObject(source)) { + for (const key in source) { + if (isObject(source[key])) { + if (!target[key]) + Object.assign(target, { [key]: {} }); + mergeDeep(target[key], source[key]); + } + else { + Object.assign(target, { [key]: source[key] }); + } + } + } + return mergeDeep(target, ...sources); + } + + const defaults = { + locked: false, + arraysMutable: false, + determineVisibility: () => window && + new URLSearchParams(window.location.search).get("mode") === "edit", + toolbarOptions: { + position: "center", + }, + }; + class Launched { + config; + renderer = new Renderer(); + addTag = () => { }; + originalTags = new Map(); + version = -1; + setCanUndo = () => { }; + setCanRedo = () => { }; + history = []; + tags = {}; + uploadImage; + Provider; + context = react.createContext({}); + static instance; + static events = new EventEmitter(); + constructor(config) { + if (Launched.instance) { + error("There can only be one instance of Launched."); + } + Launched.instance = this; + this.config = mergeDeep(defaults, config ?? {}); + this.uploadImage = this.config.onImageUpload; + this.Provider = ({ children }) => { + const [canUndo, setCanUndo] = react.useState(false); + const [canRedo, setCanRedo] = react.useState(false); + const [visible] = react.useState(() => { + const visible = this.config.determineVisibility(this); + if (!visible) + this.config.locked = true; + return visible; + }); + const [tags, setTags] = react.useState({}); + this.tags = Object.fromEntries(Object.entries(tags).map(([key, data]) => { + const setData = (value, config) => { + if (!tags[key] || this.config.locked) + return; + setTags((p) => { + const newValue = typeof value === "function" ? value(p[key].data.value) : value; + if (!config?.silent) + Launched.events.emit("tag:change", key, newValue, p[key]?.data.value); + const newTags = { ...p }; + const tag = newTags[key]; + if (tag) { + tag.data = { ...tag.data, value: newValue }; + } + return newTags; + }); + }; + return [key, { ...data, setData }]; + })); + this.addTag = (key, tag) => { + setTags((p) => ({ ...p, [key]: tag })); + }; + react.useEffect(() => { + Launched.events.emit("data:update", this.tags); + }, [tags]); + react.useEffect(() => { + this.setCanUndo = setCanUndo; + this.setCanRedo = setCanRedo; + }, []); + return (jsxRuntime.jsxs(this.context.Provider, { value: { + useTag: this.useTag.bind(this), + }, children: [children, visible && (jsxRuntime.jsx(Toolbar, { ...this.config.toolbarOptions, undo: this.undo.bind(this), redo: this.redo.bind(this), revert: this.restore.bind(this, true), save: () => this.config.save?.(Object.fromEntries(Object.entries(this.tags).map(([key, tag]) => [key, tagToValues(tag)]))), canUndo: canUndo, canRedo: canRedo }))] })); + }; + Launched.events.on("tag:ready", (...props) => { + this.render(props[0], props[2]); + }); + Launched.events.on("tag:change", (key, value, prevValue) => { + if (this.version !== this.history.length - 1) { + this.history = this.history.slice(0, this.version + 1); + this.setCanRedo(false); + } + this.version++; + this.history.push({ key: String(key), value, prevValue }); + this.setCanUndo(true); + }); + } + useTag = ((key, value, options) => { + const t = this ?? Launched.instance; + let tag = t.tags[key]; + if (!tag && value != null) { + const newTag = createTag(value, options?.type ?? + (Array.isArray(value) ? typeof value[0] : typeof value)); + setTimeout(() => this.addTag(String(key), newTag), 0); + tag = newTag; + } + else if (!tag) + error(`Tag "${String(key)}" does not exist. Try providing a value to useTag.`); + const v = typeof tag.data.value === "object" + ? flattenTagValue(tag.data.value) + : tag.data.value; + return [ + v, + (el) => { + if (!el) + return; + tag.el.current = el; + if (!this.originalTags.has(key)) + this.originalTags.set(key, tag.data.value); + const o = { + ...options, + arrayMutable: options?.arrayMutable ?? this.config.arraysMutable, + }; + Launched.events.emit("tag:ready", key, tag, o); + }, + ]; + }); + render(tag, options) { + if (!tag || !this.tags[tag]) + return; + const dry = options && this.config.locked; + this.renderer.renderSingleTagUI(this.tags[tag], String(tag), options, dry); + } + static lock() { + if (!Launched.instance) + error("Launched is not initialized."); + Launched.instance.config.locked = true; + function unmountTag(id, value, type) { + if (type === "object") { + Object.keys(value).forEach((key) => { + Launched.instance.renderer.unmountSingleTagUI(`${id}-${key}`); + }); + } + else + Launched.instance.renderer.unmountSingleTagUI(id); + } + Object.entries(Launched.instance.tags).map(([key, tag]) => { + if (!Array.isArray(tag.data.value)) + unmountTag(key, tag.data.value, tag.data.type); + else + tag.data.value.forEach((_, i) => { + unmountTag(`${key}-${i}`, tag.data.value[i], tag.data.type); + }); + }); + Launched.events.emit("data:lock"); + } + static unlock() { + if (!Launched.instance) + error("Launched is not initialized."); + Launched.instance.config.locked = false; + Object.entries(Launched.instance.tags).map(([key]) => { + Launched.instance.render(key); + }); + Launched.events.emit("data:unlock"); + } + static toggle() { + if (!Launched.instance) + error("Launched is not initialized."); + Launched.instance.config.locked ? Launched.unlock() : Launched.lock(); + } + static isVisible() { + if (!Launched.instance) + error("Launched is not initialized."); + return Launched.instance.config.determineVisibility(Launched.instance); + } + static registerTagFormat(name, renderer) { + Renderer.registerTagFormat(name, renderer); + } + undo() { + if (this.version === -1 || this.config.locked) + return; + else if (this.version === 0) { + this.version = -1; + this.restore(); + this.setCanUndo(false); + this.setCanRedo(true); + return; + } + const { key, prevValue } = this.history[this.version--]; + this.tags[key].setData(prevValue, { silent: true }); + this.setCanRedo(true); + } + redo() { + if (!this.history.length || + this.version === this.history.length - 1 || + this.config.locked) + return; + const { key, value } = this.history[++this.version]; + this.tags[key].setData(value, { silent: true }); + this.setCanUndo(true); + if (this.version === this.history.length - 1) + this.setCanRedo(false); + } + restore(hard) { + if (this.config.locked) + return; + if (hard) + this.history = []; + this.version = -1; + this.setCanUndo(false); + this.setCanRedo(false); + Array.from(this.originalTags.entries()).map(([key, value]) => { + if (this.tags[key]?.data.value !== value) { + this.tags[key].setData(value, { silent: true }); + } + }); + } + } + function LaunchedProvider({ config, children, }) { + const L = Launched.instance ?? new Launched(config); + return jsxRuntime.jsx(L.Provider, { children: children }); + } + + function useTag(key, value, options) { + if (!Launched.instance) + error("Launched not initialized."); + const { useTag } = react.useContext(Launched.instance.context); + return useTag(key, value, options); + } + + const HTMLTagsWithoutChildrenLower = [ + "area", + "base", + "br", + "col", + "embed", + "hr", + "img", + "input", + "link", + "meta", + "param", + "source", + "track", + "wbr", + ]; + const HTMLTextTagsLower = [ + "p", + "h1", + "h2", + "h3", + "h4", + "h5", + "h6", + "span", + "div", + ]; + const HTMLTagsWithoutChildren = HTMLTagsWithoutChildrenLower.map((t) => t.toUpperCase()); + const HTMLTextTags = HTMLTextTagsLower.map((t) => t.toUpperCase()); + + function InlineTextUI({ value, selected, updateData, close, }) { + const editorRef = react.useRef(null); + const [text, setText] = react.useState(value); + function sanitizeText(text) { + return text.replace(/<[^>]*>?/gm, ""); + } + function handleContentChange() { + if (!editorRef.current) + return value; + const text = sanitizeText(editorRef.current.textContent || ""); + setText(text); + return text; + } + function onClose() { + const text = handleContentChange(); + if (text !== value) + updateData(text); + close(); + } + react.useEffect(() => { + if (!editorRef.current) + return; + editorRef.current.textContent = value; + }, []); + return (jsxRuntime.jsx("div", { ref: editorRef, onInput: () => setText(editorRef.current?.textContent || ""), onBlur: onClose, className: "Launched__tag-inlineEditor", contentEditable: true, suppressContentEditableWarning: true, "data-empty": text === "", spellCheck: selected })); + } + const InlineTextRenderer = { + component: (props) => { + return jsxRuntime.jsx(InlineTextUI, { ...props }); + }, + parentValidator: (element) => { + return HTMLTextTags.includes(element.nodeName); + }, + }; + + function LinkUI({ element, value, selected, updateData, close, ...props }) { + const [href, setHref] = react.useState(value.href); + function onMouseEnter() { + element.removeAttribute("href"); + } + function onMouseLeave() { + element.setAttribute("href", href); + } + react.useEffect(() => { + element.addEventListener("mouseenter", onMouseEnter); + element.addEventListener("mouseleave", onMouseLeave); + return () => { + element.removeEventListener("mouseenter", onMouseEnter); + element.removeEventListener("mouseleave", onMouseLeave); + }; + }, []); + function onClose(e) { + if (element.contains(e.relatedTarget)) + return; + if (href !== value.href) + updateData({ ...value, href }); + close(); + } + return (jsxRuntime.jsxs("div", { onBlur: onClose, children: [selected && (jsxRuntime.jsx("div", { className: "Launched__tag-linkInput", children: jsxRuntime.jsx("input", { className: "", type: "text", value: href, placeholder: "Enter a URL...", onChange: (e) => setHref(e.target.value), onBlur: () => { + if (href !== value.href) + updateData({ ...value, href }); + } }) })), jsxRuntime.jsx(InlineTextUI, { ...props, value: value.text, updateData: (text) => updateData({ ...value, text }), element: element, selected: selected, close: () => { } })] })); + } + const LinkRenderer = { + component: (props) => { + return jsxRuntime.jsx(LinkUI, { ...props }); + }, + parentValidator: (element) => { + return element.nodeName === "A"; + }, + }; + + function ImageUI({ id, selected, context, updateData, close, }) { + async function onUpload(e) { + try { + const file = e.target.files?.[0]; + if (!file) + return; + else if (!file.type.startsWith("image/")) + return console.error("Invalid file type. Please upload an image."); + const uploadURL = await context.uploadImage?.(file); + const reader = new FileReader(); + reader.onloadend = () => { + updateData(uploadURL || reader.result); + }; + reader.readAsDataURL(file); + close(); + } + catch (e) { + console.error("Failed to upload image."); + } + } + return !selected ? null : (jsxRuntime.jsxs("div", { className: "Launched__tag-imageUpload", children: [jsxRuntime.jsx("label", { className: "Launched__button", htmlFor: `${id}-upload`, children: jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", className: "Launched__icon", children: [jsxRuntime.jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), jsxRuntime.jsx("polyline", { points: "17 8 12 3 7 8" }), jsxRuntime.jsx("line", { x1: "12", y1: "3", x2: "12", y2: "15" })] }) }), jsxRuntime.jsx("input", { id: `${id}-upload`, type: "file", onChange: onUpload, accept: "image/*" })] })); + } + const ImageRenderer = { + component: (props) => { + return jsxRuntime.jsx(ImageUI, { ...props }); + }, + parentValidator: (element) => { + const invalid = HTMLTagsWithoutChildren.includes(element.tagName); + if (invalid) + console.warn("Hint: If you're trying to attach an image tag to an IMG element, tag a wrapper element instead."); + return !invalid; + }, + }; + + Launched.registerTagFormat("string", InlineTextRenderer); + Launched.registerTagFormat("number", InlineTextRenderer); + Launched.registerTagFormat("link", LinkRenderer); + Launched.registerTagFormat("image", ImageRenderer); + + exports.LaunchedProvider = LaunchedProvider; + exports.default = Launched; + exports.useTag = useTag; + + Object.defineProperty(exports, '__esModule', { value: true }); + +})); +//# sourceMappingURL=bundle.js.map diff --git a/demo/public/scripts/bundle.js.map b/demo/public/scripts/bundle.js.map new file mode 100644 index 0000000..613186e --- /dev/null +++ b/demo/public/scripts/bundle.js.map @@ -0,0 +1 @@ +{"version":3,"file":"bundle.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"} \ No newline at end of file diff --git a/demo/public/static-demo.html b/demo/public/static-demo.html new file mode 100644 index 0000000..98804ab --- /dev/null +++ b/demo/public/static-demo.html @@ -0,0 +1,32 @@ + + + + + + + Launched + + + + + + + + + + + + + + +

Hooray

+ + diff --git a/demo/src/index.css b/demo/src/index.css index cf9d374..e846bab 100644 --- a/demo/src/index.css +++ b/demo/src/index.css @@ -827,11 +827,6 @@ video { margin-bottom: -2.5rem; } -.-mx-10{ - margin-left: -2.5rem; - margin-right: -2.5rem; -} - .-mb-10{ margin-bottom: -2.5rem; } @@ -884,10 +879,6 @@ video { height: 1.5rem; } -.h-64{ - height: 16rem; -} - .h-full{ height: 100%; } @@ -921,14 +912,6 @@ video { width: max-content; } -.max-w-48{ - max-width: 12rem; -} - -.max-w-7xl{ - max-width: 80rem; -} - .max-w-\[500px\]{ max-width: 500px; } @@ -970,10 +953,6 @@ video { grid-template-columns: repeat(2, minmax(0, 1fr)); } -.grid-cols-5{ - grid-template-columns: repeat(5, minmax(0, 1fr)); -} - .grid-cols-6{ grid-template-columns: repeat(6, minmax(0, 1fr)); } @@ -1018,14 +997,6 @@ video { gap: 1.25rem; } -.gap-6{ - gap: 1.5rem; -} - -.gap-\[2vw\]{ - gap: 2vw; -} - .overflow-hidden{ overflow: hidden; } @@ -1038,10 +1009,6 @@ video { border-radius: 1rem; } -.rounded-3xl{ - border-radius: 1.5rem; -} - .rounded-full{ border-radius: 9999px; } @@ -1106,10 +1073,6 @@ video { padding: 2.5rem; } -.p-14{ - padding: 3.5rem; -} - .p-2{ padding: 0.5rem; } @@ -1128,11 +1091,6 @@ video { padding-right: 0.75rem; } -.px-6{ - padding-left: 1.5rem; - padding-right: 1.5rem; -} - .py-2{ padding-top: 0.5rem; padding-bottom: 0.5rem; @@ -1163,18 +1121,10 @@ video { padding-top: 2.5rem; } -.pt-20{ - padding-top: 5rem; -} - .text-center{ text-align: center; } -.font-sans{ - font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; -} - .text-3xl{ font-size: 1.875rem; line-height: 2.25rem; @@ -1200,11 +1150,6 @@ video { line-height: 1.25rem; } -.text-sm\/\[19\.6px\]{ - font-size: 0.875rem; - line-height: 19.6px; -} - .text-xl{ font-size: 1.25rem; line-height: 1.75rem; @@ -1370,8 +1315,9 @@ video { padding: 2.5rem; } - .sm\:p-20{ - padding: 5rem; + .sm\:px-10{ + padding-left: 2.5rem; + padding-right: 2.5rem; } .sm\:py-1{ @@ -1384,11 +1330,6 @@ video { padding-bottom: 10rem; } - .sm\:px-10{ - padding-left: 2.5rem; - padding-right: 2.5rem; - } - .sm\:pt-20{ padding-top: 5rem; } diff --git a/package-lock.json b/package-lock.json index be32194..147049b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "launched", - "version": "0.1.0-alpha.2", + "version": "0.1.1-alpha.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "launched", - "version": "0.1.0-alpha.2", + "version": "0.1.1-alpha.1", "license": "ISC", "dependencies": { "react": "^18.3.1", @@ -23,6 +23,9 @@ "nodemon": "^3.1.2", "prettier": "^3.2.5", "rimraf": "^5.0.7", + "rollup": "^4.19.0", + "rollup-plugin-ignore-import": "^1.3.2", + "rollup-plugin-typescript2": "^0.36.0", "ts-node": "^10.9.2", "tsc-alias": "^1.8.10", "typescript": "^5.4.5", @@ -796,10 +799,29 @@ "node": ">=14" } }, + "node_modules/@rollup/pluginutils": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", + "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", + "dev": true, + "dependencies": { + "estree-walker": "^2.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", - "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.19.0.tgz", + "integrity": "sha512-JlPfZ/C7yn5S5p0yKk7uhHTTnFlvTgLetl2VxqE518QgyM7C9bSfFTYvB/Q/ftkq0RIPY4ySxTz+/wKJ/dXC0w==", "cpu": [ "arm" ], @@ -810,9 +832,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", - "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.19.0.tgz", + "integrity": "sha512-RDxUSY8D1tWYfn00DDi5myxKgOk6RvWPxhmWexcICt/MEC6yEMr4HNCu1sXXYLw8iAsg0D44NuU+qNq7zVWCrw==", "cpu": [ "arm64" ], @@ -823,9 +845,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", - "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.19.0.tgz", + "integrity": "sha512-emvKHL4B15x6nlNTBMtIaC9tLPRpeA5jMvRLXVbl/W9Ie7HhkrE7KQjvgS9uxgatL1HmHWDXk5TTS4IaNJxbAA==", "cpu": [ "arm64" ], @@ -836,9 +858,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", - "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.19.0.tgz", + "integrity": "sha512-fO28cWA1dC57qCd+D0rfLC4VPbh6EOJXrreBmFLWPGI9dpMlER2YwSPZzSGfq11XgcEpPukPTfEVFtw2q2nYJg==", "cpu": [ "x64" ], @@ -849,9 +871,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", - "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.19.0.tgz", + "integrity": "sha512-2Rn36Ubxdv32NUcfm0wB1tgKqkQuft00PtM23VqLuCUR4N5jcNWDoV5iBC9jeGdgS38WK66ElncprqgMUOyomw==", "cpu": [ "arm" ], @@ -862,9 +884,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", - "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.19.0.tgz", + "integrity": "sha512-gJuzIVdq/X1ZA2bHeCGCISe0VWqCoNT8BvkQ+BfsixXwTOndhtLUpOg0A1Fcx/+eA6ei6rMBzlOz4JzmiDw7JQ==", "cpu": [ "arm" ], @@ -875,9 +897,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", - "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.19.0.tgz", + "integrity": "sha512-0EkX2HYPkSADo9cfeGFoQ7R0/wTKb7q6DdwI4Yn/ULFE1wuRRCHybxpl2goQrx4c/yzK3I8OlgtBu4xvted0ug==", "cpu": [ "arm64" ], @@ -888,9 +910,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", - "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.19.0.tgz", + "integrity": "sha512-GlIQRj9px52ISomIOEUq/IojLZqzkvRpdP3cLgIE1wUWaiU5Takwlzpz002q0Nxxr1y2ZgxC2obWxjr13lvxNQ==", "cpu": [ "arm64" ], @@ -901,9 +923,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", - "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.19.0.tgz", + "integrity": "sha512-N6cFJzssruDLUOKfEKeovCKiHcdwVYOT1Hs6dovDQ61+Y9n3Ek4zXvtghPPelt6U0AH4aDGnDLb83uiJMkWYzQ==", "cpu": [ "ppc64" ], @@ -914,9 +936,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", - "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.19.0.tgz", + "integrity": "sha512-2DnD3mkS2uuam/alF+I7M84koGwvn3ZVD7uG+LEWpyzo/bq8+kKnus2EVCkcvh6PlNB8QPNFOz6fWd5N8o1CYg==", "cpu": [ "riscv64" ], @@ -927,9 +949,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", - "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.19.0.tgz", + "integrity": "sha512-D6pkaF7OpE7lzlTOFCB2m3Ngzu2ykw40Nka9WmKGUOTS3xcIieHe82slQlNq69sVB04ch73thKYIWz/Ian8DUA==", "cpu": [ "s390x" ], @@ -940,9 +962,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", - "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.19.0.tgz", + "integrity": "sha512-HBndjQLP8OsdJNSxpNIN0einbDmRFg9+UQeZV1eiYupIRuZsDEoeGU43NQsS34Pp166DtwQOnpcbV/zQxM+rWA==", "cpu": [ "x64" ], @@ -953,9 +975,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", - "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.19.0.tgz", + "integrity": "sha512-HxfbvfCKJe/RMYJJn0a12eiOI9OOtAUF4G6ozrFUK95BNyoJaSiBjIOHjZskTUffUrB84IPKkFG9H9nEvJGW6A==", "cpu": [ "x64" ], @@ -966,9 +988,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", - "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.19.0.tgz", + "integrity": "sha512-HxDMKIhmcguGTiP5TsLNolwBUK3nGGUEoV/BO9ldUBoMLBssvh4J0X8pf11i1fTV7WShWItB1bKAKjX4RQeYmg==", "cpu": [ "arm64" ], @@ -979,9 +1001,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", - "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.19.0.tgz", + "integrity": "sha512-xItlIAZZaiG/u0wooGzRsx11rokP4qyc/79LkAOdznGRAbOFc+SfEdfUOszG1odsHNgwippUJavag/+W/Etc6Q==", "cpu": [ "ia32" ], @@ -992,9 +1014,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", - "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.19.0.tgz", + "integrity": "sha512-xNo5fV5ycvCCKqiZcpB65VMR11NJB+StnxHz20jdqRAktfdfzhgjTiJ2doTDQE/7dqGaV5I7ZGqKpgph6lCIag==", "cpu": [ "x64" ], @@ -1604,6 +1626,12 @@ "node": "^12.20.0 || >=14" } }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, "node_modules/concat-map": { "version": "0.0.1", "dev": true, @@ -1902,6 +1930,60 @@ "node": ">=8" } }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-cache-dir/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-cache-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/foreground-child": { "version": "3.1.1", "dev": true, @@ -1931,6 +2013,29 @@ "node": ">= 6" } }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-extra/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2023,6 +2128,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2322,6 +2433,27 @@ } } }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonfile/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/local-pkg": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", @@ -2338,6 +2470,18 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -2679,6 +2823,42 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/parse5": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", @@ -2691,6 +2871,15 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -2772,6 +2961,18 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/pkg-types": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.1.tgz", @@ -3070,9 +3271,9 @@ } }, "node_modules/rollup": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", - "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.19.0.tgz", + "integrity": "sha512-5r7EYSQIowHsK4eTZ0Y81qpZuJz+MUuYeqmmYmRMl1nwhdmbiYqt5jwzf6u7wyOzJgYqtCRMtVRKOtHANBz7rA==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -3085,25 +3286,69 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.18.0", - "@rollup/rollup-android-arm64": "4.18.0", - "@rollup/rollup-darwin-arm64": "4.18.0", - "@rollup/rollup-darwin-x64": "4.18.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", - "@rollup/rollup-linux-arm-musleabihf": "4.18.0", - "@rollup/rollup-linux-arm64-gnu": "4.18.0", - "@rollup/rollup-linux-arm64-musl": "4.18.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", - "@rollup/rollup-linux-riscv64-gnu": "4.18.0", - "@rollup/rollup-linux-s390x-gnu": "4.18.0", - "@rollup/rollup-linux-x64-gnu": "4.18.0", - "@rollup/rollup-linux-x64-musl": "4.18.0", - "@rollup/rollup-win32-arm64-msvc": "4.18.0", - "@rollup/rollup-win32-ia32-msvc": "4.18.0", - "@rollup/rollup-win32-x64-msvc": "4.18.0", + "@rollup/rollup-android-arm-eabi": "4.19.0", + "@rollup/rollup-android-arm64": "4.19.0", + "@rollup/rollup-darwin-arm64": "4.19.0", + "@rollup/rollup-darwin-x64": "4.19.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.19.0", + "@rollup/rollup-linux-arm-musleabihf": "4.19.0", + "@rollup/rollup-linux-arm64-gnu": "4.19.0", + "@rollup/rollup-linux-arm64-musl": "4.19.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.19.0", + "@rollup/rollup-linux-riscv64-gnu": "4.19.0", + "@rollup/rollup-linux-s390x-gnu": "4.19.0", + "@rollup/rollup-linux-x64-gnu": "4.19.0", + "@rollup/rollup-linux-x64-musl": "4.19.0", + "@rollup/rollup-win32-arm64-msvc": "4.19.0", + "@rollup/rollup-win32-ia32-msvc": "4.19.0", + "@rollup/rollup-win32-x64-msvc": "4.19.0", "fsevents": "~2.3.2" } }, + "node_modules/rollup-plugin-ignore-import": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-ignore-import/-/rollup-plugin-ignore-import-1.3.2.tgz", + "integrity": "sha512-q7yH2c+PKVfb61+MTXqqyBHIgflikumC7OEB+OfQWNsSmDqE5FLZLeewcBGl1VDmjDjSXuALXsaBjyIsl3oNmQ==", + "dev": true, + "peerDependencies": { + "rollup": ">=0.62.0", + "rollup-pluginutils": "^2.8.2" + } + }, + "node_modules/rollup-plugin-typescript2": { + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.36.0.tgz", + "integrity": "sha512-NB2CSQDxSe9+Oe2ahZbf+B4bh7pHwjV5L+RSYpCu7Q5ROuN94F9b6ioWwKfz3ueL3KTtmX4o2MUH2cgHDIEUsw==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^4.1.2", + "find-cache-dir": "^3.3.2", + "fs-extra": "^10.0.0", + "semver": "^7.5.4", + "tslib": "^2.6.2" + }, + "peerDependencies": { + "rollup": ">=1.26.3", + "typescript": ">=2.4.0" + } + }, + "node_modules/rollup-pluginutils": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", + "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", + "dev": true, + "peer": true, + "dependencies": { + "estree-walker": "^0.6.1" + } + }, + "node_modules/rollup-pluginutils/node_modules/estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "dev": true, + "peer": true + }, "node_modules/rrweb-cssom": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", @@ -3513,6 +3758,12 @@ "tsc-alias": "dist/bin/index.js" } }, + "node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", diff --git a/package.json b/package.json index 88b1b29..4176bc1 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,9 @@ "nodemon": "^3.1.2", "prettier": "^3.2.5", "rimraf": "^5.0.7", + "rollup": "^4.19.0", + "rollup-plugin-ignore-import": "^1.3.2", + "rollup-plugin-typescript2": "^0.36.0", "ts-node": "^10.9.2", "tsc-alias": "^1.8.10", "typescript": "^5.4.5", diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..fbb2567 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,32 @@ +import typescript from "rollup-plugin-typescript2"; +import ignoreImport from "rollup-plugin-ignore-import"; + +export default { + input: "src/index.ts", + output: { + file: "dist/bundle.js", + format: "umd", + name: "launched", + sourcemap: true, + exports: "named", + globals: { + react: "React", + "react-dom/client": "ReactDOM", + "react/jsx-runtime": "jsxRuntime", + }, + }, + plugins: [ + typescript({ + tsconfig: "./tsconfig.json", + tsconfigOverride: { + compilerOptions: { + declaration: false, + }, + }, + useTsconfigDeclarationDir: true, + }), + ignoreImport({ + extensions: [".css"], + }), + ], +}; diff --git a/scripts/build.sh b/scripts/build.sh index b2a1e0e..825983e 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -13,7 +13,9 @@ if [ -d "$DIST" ]; then fi mkdir "$DIST" +echo "----------------------------------------" echo "Copying files..." +echo "----------------------------------------" shopt -s globstar for file in src/**/*.css; do @@ -24,6 +26,14 @@ for file in src/**/*.css; do cp "$source" "$destination" done +echo "----------------------------------------" +echo "Generating UMD bundle..." +echo "----------------------------------------" + +npx rollup -c rollup.config.js || { echo "UMD build failed"; exit 1; } + +echo "----------------------------------------" echo "Building project..." +echo "----------------------------------------" -npx tsc --outDir "$DIST" && npx tsc-alias --outDir "$DIST" || { echo "Build failed"; exit 1; } +npx tsc --outDir "$DIST" && npx tsc-alias --outDir "$DIST" || { echo "TSC build failed"; exit 1; } From 96162b537369609fd52db43ef3bf84ea31736c02 Mon Sep 17 00:00:00 2001 From: MMMJB Date: Wed, 24 Jul 2024 17:05:05 -0400 Subject: [PATCH 2/4] build: name default export as Launched --- demo/public/scripts/bundle.js | 737 ------------------------------ demo/public/scripts/bundle.js.map | 1 - demo/public/static-demo.html | 80 +++- rollup.config.js | 3 + 4 files changed, 80 insertions(+), 741 deletions(-) delete mode 100644 demo/public/scripts/bundle.js delete mode 100644 demo/public/scripts/bundle.js.map diff --git a/demo/public/scripts/bundle.js b/demo/public/scripts/bundle.js deleted file mode 100644 index ad3ffff..0000000 --- a/demo/public/scripts/bundle.js +++ /dev/null @@ -1,737 +0,0 @@ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react/jsx-runtime'), require('react'), require('react-dom/client')) : - typeof define === 'function' && define.amd ? define(['exports', 'react/jsx-runtime', 'react', 'react-dom/client'], factory) : - (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.launched = {}, global.jsxRuntime, global.React, global.ReactDOM)); -})(this, (function (exports, jsxRuntime, react, client) { 'use strict'; - - class EventEmitter { - events; - constructor() { - this.events = {}; - } - on(event, listener) { - if (!this.events[event]) { - this.events[event] = []; - } - this.events[event].push(listener); - } - off(event, listener) { - if (!this.events[event]) - return; - this.events[event] = this.events[event].filter((l) => l !== listener); - } - emit(event, ...args) { - if (!this.events[event]) - return; - this.events[event].forEach((listener) => listener(...args)); - } - } - // Events: - // tag:ready: a tag is bound to an element => id, tag - // tag:mount: tag UI component renders => id, tag - // tag:unmount: tag UI component unmounts => id, tag - // tag:select: tag UI gains focus => id, tag - // tag:deselect: tag UI loses focus => id, tag - // tag:change: a single tag is updated => key, newValue, originalValue - // data:update: tag data changes => newTagData - // data:lock: tag data is locked - // data:unlock: tag data is unlocked - - function error(msg) { - throw new Error(`Launched error: ${msg}`); - } - - function flattenTagValue(value) { - if (Array.isArray(value)) - return value.map((v) => flattenTagValue(v)); - else if (typeof value === "object") { - return Object.fromEntries(Object.entries(value).map(([key, v]) => { - if (typeof v === "object" && "value" in v) - return [key, flattenTagValue(v.value)]; - else - return [key, flattenTagValue(v)]; - })); - } - else - return value; - } - - class Renderer { - static formats = new Map(); - static roots = new Map(); - initialRenderOptions = new Map(); - constructor() { } - static registerTagFormat(name, renderer) { - if (!renderer.component) - error("Custom renderers must have a component."); - Renderer.formats.set(name, renderer); - } - renderSingleTagUI(parentTag, id, options, dry) { - if (!parentTag || !parentTag.el.current) - return console.warn(`Tag "${id}" was never bound to an element.`); - const renderTag = (parentTag, tag, childId, index) => { - if (!tag.el.current) - return; - if (Array.isArray(tag.data.value)) { - tag.data.value.forEach((t, i) => { - const childEl = tag.el.current.children[i] ?? tag.el.current; - renderTag(parentTag, { - el: { current: childEl }, - data: { - type: tag.data.type, - value: t, - }, - setData: (data) => { - tag.setData(tag.data.value.map((v, index) => index === i - ? (typeof data === "function" - ? data(v) - : data) - : v)); - }, - }, `${id}-${i}`, i); - }); - } - else if (tag.data.type === "object" && - typeof tag.data.value === "object") { - for (const key in tag.data.value) { - const childEl = tag.el.current.querySelector(`[data-key="${key}"]`); - if (!childEl) - error(`Child element with key "${key}" (under "${id}") not found. If you're using a custom renderer, make sure to add a data-key attribute to the targeted element.`); - renderTag(parentTag, { - el: { current: childEl }, - data: { - type: tag.data.value[key].type, - value: tag.data.value[key].value, - }, - setData: (data) => { - const newValue = typeof data === "function" - ? data(tag.data.value[key].value) - : data; - tag.setData({ - ...tag.data.value, - [key]: { - type: tag.data.value[key] - .type, - value: newValue, - }, - }); - }, - }, `${childId}-${key}`, index); - } - } - else { - if (!tag.el.current) - return; - const renderer = Renderer.formats.get(tag.data.type); - if (!renderer) { - return console.warn(`No renderer found for tag type "${tag.data.type}".`); - } - if (renderer.parentValidator && - !renderer.parentValidator(tag.el.current)) { - return console.warn(`Parent element of tag "${childId}" does not satisfy the constraints of the "${tag.data.type}" renderer.`); - } - const id = `Lt-${childId.replaceAll(" ", "-")}`; - let userOptions; - if (this.initialRenderOptions.get(id)) - userOptions = this.initialRenderOptions.get(id); - else { - userOptions = { - arrayMutable: options?.arrayMutable ?? false, - index, - parentTag, - }; - this.initialRenderOptions.set(id, userOptions); - } - if (dry) - return; - const existingNode = document.getElementById(id); - if (existingNode) - existingNode.remove(); - setTimeout(() => { - if (Renderer.roots.get(childId)) { - Renderer.roots.get(childId).unmount(); - Renderer.roots.delete(childId); - } - const rootNode = document.createElement("div"); - rootNode.id = id; - tag.el.current.appendChild(rootNode); - const root = client.createRoot(rootNode); - Renderer.roots.set(childId, root); - const t = { - ...tag, - data: { - type: tag.data.type, - value: flattenTagValue(tag.data.value), - }, - }; - root.render(jsxRuntime.jsx(TagUI, { tag: t, renderer: renderer, id: childId, options: userOptions })); - }, 0); - } - }; - renderTag(parentTag, parentTag, id, 0); - } - unmountSingleTagUI(tagId) { - const id = `Lt-${tagId.split(" ").join("-")}`; - const root = Renderer.roots.get(tagId); - if (root) { - root.unmount(); - Renderer.roots.delete(tagId); - } - const node = document.getElementById(id); - if (node) - node.remove(); - } - } - function TagUI({ tag, renderer, id, options, }) { - const containerRef = react.useRef(null); - const [selected, setSelected] = react.useState(false); - const { arrayMutable, parentTag, index } = options; - function close() { - setSelected(false); - containerRef.current?.blur(); - renderer.onClose?.({ - element: tag.el.current ?? undefined, - }); - Launched.events.emit("tag:deselect", id, tag); - } - function updateData(data) { - tag.setData(data); - renderer.onDataUpdate?.({ - element: tag.el.current ?? undefined, - data, - }); - // @ts-expect-error - tag.el.current = null; - } - function duplicateTagItem() { - if (!Array.isArray(parentTag.data.value)) - return; - parentTag.setData((p) => [ - ...p.slice(0, index + 1), - p[index], - ...p.slice(index + 1), - ]); - } - function removeTagItem() { - if (!Array.isArray(parentTag.data.value)) - return; - parentTag.setData((p) => [ - ...p.slice(0, index), - ...p.slice(index + 1), - ]); - } - function onTagSelect(selectedId) { - if (selectedId !== id) - setSelected(false); - } - react.useEffect(() => { - if (!tag.el.current) - error("Element is null."); - tag.el.current.classList.add("tagged"); - if (getComputedStyle(tag.el.current).position === "static") { - tag.el.current.style.position = "relative"; - } - Launched.events.emit("tag:mount", id, tag); - Launched.events.on("tag:select", onTagSelect); - return () => { - Launched.events.emit("tag:unmount", id, tag); - Launched.events.off("tag:select", onTagSelect); - }; - }, []); - function select() { - if (selected) - return; - setSelected(true); - renderer?.onSelect?.({ element: tag.el.current }); - Launched.events.emit("tag:select", id, tag); - } - if (!tag.el.current) - return null; - return (jsxRuntime.jsxs("div", { ref: containerRef, tabIndex: 0, onMouseDown: select, onFocus: select, className: `Launched__tag-container ${selected && "active"}`, children: [jsxRuntime.jsx(renderer.component, { element: tag.el.current, value: tag.data.value, selected: selected, updateData: (v) => updateData(v), close: () => close(), id: id, context: Launched.instance }), arrayMutable && (jsxRuntime.jsxs("div", { onMouseDown: (e) => e.preventDefault(), className: "Launched__tag-arrayControls Launched__toolbar-tools", children: [jsxRuntime.jsx("button", { className: "Launched__toolbar-button add", onClick: duplicateTagItem, children: jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", className: "Launched__icon", children: [jsxRuntime.jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }), jsxRuntime.jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })] }) }), jsxRuntime.jsx("button", { className: "Launched__button remove", onClick: removeTagItem, children: jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", className: "Launched__icon", children: [jsxRuntime.jsx("polyline", { points: "3 6 5 6 21 6" }), jsxRuntime.jsx("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })] }) })] }))] })); - } - - function Toolbar({ position, className, canUndo, canRedo, undo, redo, save, revert, }) { - // @ts-expect-error - const [disabled, setDisabled] = react.useState(Launched.instance.config.locked); - return (jsxRuntime.jsx("div", { "data-position": position, className: `Launched__toolbar ${className || ""} ${disabled && "disabled"}`, children: jsxRuntime.jsxs("div", { className: "Launched__toolbar-tools", children: [jsxRuntime.jsx("button", { onClick: save, className: "Launched__toolbar-saveButton Launched__button", children: "Save" }), jsxRuntime.jsxs("select", { onChange: (e) => { - if (e.target.value === "locked") { - Launched.lock(); - setDisabled(true); - } - else { - Launched.unlock(); - setDisabled(false); - } - }, className: "Launched__toolbar-lockMode", value: disabled ? "locked" : "unlocked", children: [jsxRuntime.jsx("option", { value: "unlocked", children: "Edit" }), jsxRuntime.jsx("option", { value: "locked", children: "Preview" })] }), jsxRuntime.jsx("button", { disabled: !canUndo, onClick: undo, className: "Launched__toolbar-button undo", children: jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", className: "Launched__icon", children: [jsxRuntime.jsx("polyline", { points: "1 4 1 10 7 10" }), jsxRuntime.jsx("path", { d: "M3.51 15a9 9 0 1 0 2.13-9.36L1 10" })] }) }), jsxRuntime.jsx("button", { disabled: !canRedo, onClick: redo, className: "Launched__toolbar-button redo", children: jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", className: "Launched__icon", children: [jsxRuntime.jsx("polyline", { points: "23 4 23 10 17 10" }), jsxRuntime.jsx("path", { d: "M20.49 15a9 9 0 1 1-2.12-9.36L23 10" })] }) }), jsxRuntime.jsx("button", { onClick: revert, className: "Launched__toolbar-revertButton Launched__button", children: "Revert" })] }) })); - } - - function validateObject(tag) { - if ("type" in tag && "value" in tag) - return; - if (Object.values(tag).some((v) => typeof v === "object" && !("type" in v) && !("value" in v))) { - error("Objects cannot have nested objects without an explicit type."); - } - } - function transformObjectToTagData(value, type) { - if (typeof value === "object" && "type" in value && "value" in value) - return value; - return Object.fromEntries(Object.entries(value).map(([k, v]) => { - if (typeof v !== "object") - return [k, { type: typeof v, value: v }]; - else - return [k, transformObjectToTagData(v)]; - })); - } - function transformTag(tag, type) { - if (Array.isArray(tag)) { - if (!tag.length) - return { type, value: [] }; - else if (tag.some((v) => typeof v !== typeof tag[0])) - error("Array must have items of the same type."); - if (typeof tag[0] === "object") { - if (tag.some((v) => Array.isArray(v))) - error("Array cannot have nested arrays."); - const keys = tag.map((v) => Object.keys(v)); - if (keys[0].some((key) => keys.some((k) => !k.includes(key)))) - error("Objects must have the same keys."); - validateObject(tag[0]); - return { - type, - value: tag.map((v) => transformObjectToTagData(v)), - }; - } - else - return { - type, - value: tag, - }; - } - else if (typeof tag === "object") { - validateObject(tag); - const value = transformObjectToTagData(tag); - return { - type, - value, - }; - } - else { - return { - type, - value: tag, - }; - } - } - function createTag(tag, type) { - const t = transformTag(tag, type); - return { - el: react.createRef(), - data: t, - }; - } - - function tagToValues(tag) { - if (Array.isArray(tag.data.value)) { - return tag.data.value.map((t) => tagToValues({ - ...tag, - data: { - ...tag.data, - value: t, - }, - })); - } - else if (typeof tag.data.value === "object") { - // @ts-expect-error - return flattenTagValue(tag.data.value); - } - else - return tag.data.value; - } - - function mergeDeep(target, ...sources) { - const isObject = (t) => t && typeof t === "object" && !Array.isArray(t); - if (!sources.length) - return target; - const source = sources.shift(); - if (isObject(target) && isObject(source)) { - for (const key in source) { - if (isObject(source[key])) { - if (!target[key]) - Object.assign(target, { [key]: {} }); - mergeDeep(target[key], source[key]); - } - else { - Object.assign(target, { [key]: source[key] }); - } - } - } - return mergeDeep(target, ...sources); - } - - const defaults = { - locked: false, - arraysMutable: false, - determineVisibility: () => window && - new URLSearchParams(window.location.search).get("mode") === "edit", - toolbarOptions: { - position: "center", - }, - }; - class Launched { - config; - renderer = new Renderer(); - addTag = () => { }; - originalTags = new Map(); - version = -1; - setCanUndo = () => { }; - setCanRedo = () => { }; - history = []; - tags = {}; - uploadImage; - Provider; - context = react.createContext({}); - static instance; - static events = new EventEmitter(); - constructor(config) { - if (Launched.instance) { - error("There can only be one instance of Launched."); - } - Launched.instance = this; - this.config = mergeDeep(defaults, config ?? {}); - this.uploadImage = this.config.onImageUpload; - this.Provider = ({ children }) => { - const [canUndo, setCanUndo] = react.useState(false); - const [canRedo, setCanRedo] = react.useState(false); - const [visible] = react.useState(() => { - const visible = this.config.determineVisibility(this); - if (!visible) - this.config.locked = true; - return visible; - }); - const [tags, setTags] = react.useState({}); - this.tags = Object.fromEntries(Object.entries(tags).map(([key, data]) => { - const setData = (value, config) => { - if (!tags[key] || this.config.locked) - return; - setTags((p) => { - const newValue = typeof value === "function" ? value(p[key].data.value) : value; - if (!config?.silent) - Launched.events.emit("tag:change", key, newValue, p[key]?.data.value); - const newTags = { ...p }; - const tag = newTags[key]; - if (tag) { - tag.data = { ...tag.data, value: newValue }; - } - return newTags; - }); - }; - return [key, { ...data, setData }]; - })); - this.addTag = (key, tag) => { - setTags((p) => ({ ...p, [key]: tag })); - }; - react.useEffect(() => { - Launched.events.emit("data:update", this.tags); - }, [tags]); - react.useEffect(() => { - this.setCanUndo = setCanUndo; - this.setCanRedo = setCanRedo; - }, []); - return (jsxRuntime.jsxs(this.context.Provider, { value: { - useTag: this.useTag.bind(this), - }, children: [children, visible && (jsxRuntime.jsx(Toolbar, { ...this.config.toolbarOptions, undo: this.undo.bind(this), redo: this.redo.bind(this), revert: this.restore.bind(this, true), save: () => this.config.save?.(Object.fromEntries(Object.entries(this.tags).map(([key, tag]) => [key, tagToValues(tag)]))), canUndo: canUndo, canRedo: canRedo }))] })); - }; - Launched.events.on("tag:ready", (...props) => { - this.render(props[0], props[2]); - }); - Launched.events.on("tag:change", (key, value, prevValue) => { - if (this.version !== this.history.length - 1) { - this.history = this.history.slice(0, this.version + 1); - this.setCanRedo(false); - } - this.version++; - this.history.push({ key: String(key), value, prevValue }); - this.setCanUndo(true); - }); - } - useTag = ((key, value, options) => { - const t = this ?? Launched.instance; - let tag = t.tags[key]; - if (!tag && value != null) { - const newTag = createTag(value, options?.type ?? - (Array.isArray(value) ? typeof value[0] : typeof value)); - setTimeout(() => this.addTag(String(key), newTag), 0); - tag = newTag; - } - else if (!tag) - error(`Tag "${String(key)}" does not exist. Try providing a value to useTag.`); - const v = typeof tag.data.value === "object" - ? flattenTagValue(tag.data.value) - : tag.data.value; - return [ - v, - (el) => { - if (!el) - return; - tag.el.current = el; - if (!this.originalTags.has(key)) - this.originalTags.set(key, tag.data.value); - const o = { - ...options, - arrayMutable: options?.arrayMutable ?? this.config.arraysMutable, - }; - Launched.events.emit("tag:ready", key, tag, o); - }, - ]; - }); - render(tag, options) { - if (!tag || !this.tags[tag]) - return; - const dry = options && this.config.locked; - this.renderer.renderSingleTagUI(this.tags[tag], String(tag), options, dry); - } - static lock() { - if (!Launched.instance) - error("Launched is not initialized."); - Launched.instance.config.locked = true; - function unmountTag(id, value, type) { - if (type === "object") { - Object.keys(value).forEach((key) => { - Launched.instance.renderer.unmountSingleTagUI(`${id}-${key}`); - }); - } - else - Launched.instance.renderer.unmountSingleTagUI(id); - } - Object.entries(Launched.instance.tags).map(([key, tag]) => { - if (!Array.isArray(tag.data.value)) - unmountTag(key, tag.data.value, tag.data.type); - else - tag.data.value.forEach((_, i) => { - unmountTag(`${key}-${i}`, tag.data.value[i], tag.data.type); - }); - }); - Launched.events.emit("data:lock"); - } - static unlock() { - if (!Launched.instance) - error("Launched is not initialized."); - Launched.instance.config.locked = false; - Object.entries(Launched.instance.tags).map(([key]) => { - Launched.instance.render(key); - }); - Launched.events.emit("data:unlock"); - } - static toggle() { - if (!Launched.instance) - error("Launched is not initialized."); - Launched.instance.config.locked ? Launched.unlock() : Launched.lock(); - } - static isVisible() { - if (!Launched.instance) - error("Launched is not initialized."); - return Launched.instance.config.determineVisibility(Launched.instance); - } - static registerTagFormat(name, renderer) { - Renderer.registerTagFormat(name, renderer); - } - undo() { - if (this.version === -1 || this.config.locked) - return; - else if (this.version === 0) { - this.version = -1; - this.restore(); - this.setCanUndo(false); - this.setCanRedo(true); - return; - } - const { key, prevValue } = this.history[this.version--]; - this.tags[key].setData(prevValue, { silent: true }); - this.setCanRedo(true); - } - redo() { - if (!this.history.length || - this.version === this.history.length - 1 || - this.config.locked) - return; - const { key, value } = this.history[++this.version]; - this.tags[key].setData(value, { silent: true }); - this.setCanUndo(true); - if (this.version === this.history.length - 1) - this.setCanRedo(false); - } - restore(hard) { - if (this.config.locked) - return; - if (hard) - this.history = []; - this.version = -1; - this.setCanUndo(false); - this.setCanRedo(false); - Array.from(this.originalTags.entries()).map(([key, value]) => { - if (this.tags[key]?.data.value !== value) { - this.tags[key].setData(value, { silent: true }); - } - }); - } - } - function LaunchedProvider({ config, children, }) { - const L = Launched.instance ?? new Launched(config); - return jsxRuntime.jsx(L.Provider, { children: children }); - } - - function useTag(key, value, options) { - if (!Launched.instance) - error("Launched not initialized."); - const { useTag } = react.useContext(Launched.instance.context); - return useTag(key, value, options); - } - - const HTMLTagsWithoutChildrenLower = [ - "area", - "base", - "br", - "col", - "embed", - "hr", - "img", - "input", - "link", - "meta", - "param", - "source", - "track", - "wbr", - ]; - const HTMLTextTagsLower = [ - "p", - "h1", - "h2", - "h3", - "h4", - "h5", - "h6", - "span", - "div", - ]; - const HTMLTagsWithoutChildren = HTMLTagsWithoutChildrenLower.map((t) => t.toUpperCase()); - const HTMLTextTags = HTMLTextTagsLower.map((t) => t.toUpperCase()); - - function InlineTextUI({ value, selected, updateData, close, }) { - const editorRef = react.useRef(null); - const [text, setText] = react.useState(value); - function sanitizeText(text) { - return text.replace(/<[^>]*>?/gm, ""); - } - function handleContentChange() { - if (!editorRef.current) - return value; - const text = sanitizeText(editorRef.current.textContent || ""); - setText(text); - return text; - } - function onClose() { - const text = handleContentChange(); - if (text !== value) - updateData(text); - close(); - } - react.useEffect(() => { - if (!editorRef.current) - return; - editorRef.current.textContent = value; - }, []); - return (jsxRuntime.jsx("div", { ref: editorRef, onInput: () => setText(editorRef.current?.textContent || ""), onBlur: onClose, className: "Launched__tag-inlineEditor", contentEditable: true, suppressContentEditableWarning: true, "data-empty": text === "", spellCheck: selected })); - } - const InlineTextRenderer = { - component: (props) => { - return jsxRuntime.jsx(InlineTextUI, { ...props }); - }, - parentValidator: (element) => { - return HTMLTextTags.includes(element.nodeName); - }, - }; - - function LinkUI({ element, value, selected, updateData, close, ...props }) { - const [href, setHref] = react.useState(value.href); - function onMouseEnter() { - element.removeAttribute("href"); - } - function onMouseLeave() { - element.setAttribute("href", href); - } - react.useEffect(() => { - element.addEventListener("mouseenter", onMouseEnter); - element.addEventListener("mouseleave", onMouseLeave); - return () => { - element.removeEventListener("mouseenter", onMouseEnter); - element.removeEventListener("mouseleave", onMouseLeave); - }; - }, []); - function onClose(e) { - if (element.contains(e.relatedTarget)) - return; - if (href !== value.href) - updateData({ ...value, href }); - close(); - } - return (jsxRuntime.jsxs("div", { onBlur: onClose, children: [selected && (jsxRuntime.jsx("div", { className: "Launched__tag-linkInput", children: jsxRuntime.jsx("input", { className: "", type: "text", value: href, placeholder: "Enter a URL...", onChange: (e) => setHref(e.target.value), onBlur: () => { - if (href !== value.href) - updateData({ ...value, href }); - } }) })), jsxRuntime.jsx(InlineTextUI, { ...props, value: value.text, updateData: (text) => updateData({ ...value, text }), element: element, selected: selected, close: () => { } })] })); - } - const LinkRenderer = { - component: (props) => { - return jsxRuntime.jsx(LinkUI, { ...props }); - }, - parentValidator: (element) => { - return element.nodeName === "A"; - }, - }; - - function ImageUI({ id, selected, context, updateData, close, }) { - async function onUpload(e) { - try { - const file = e.target.files?.[0]; - if (!file) - return; - else if (!file.type.startsWith("image/")) - return console.error("Invalid file type. Please upload an image."); - const uploadURL = await context.uploadImage?.(file); - const reader = new FileReader(); - reader.onloadend = () => { - updateData(uploadURL || reader.result); - }; - reader.readAsDataURL(file); - close(); - } - catch (e) { - console.error("Failed to upload image."); - } - } - return !selected ? null : (jsxRuntime.jsxs("div", { className: "Launched__tag-imageUpload", children: [jsxRuntime.jsx("label", { className: "Launched__button", htmlFor: `${id}-upload`, children: jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", className: "Launched__icon", children: [jsxRuntime.jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), jsxRuntime.jsx("polyline", { points: "17 8 12 3 7 8" }), jsxRuntime.jsx("line", { x1: "12", y1: "3", x2: "12", y2: "15" })] }) }), jsxRuntime.jsx("input", { id: `${id}-upload`, type: "file", onChange: onUpload, accept: "image/*" })] })); - } - const ImageRenderer = { - component: (props) => { - return jsxRuntime.jsx(ImageUI, { ...props }); - }, - parentValidator: (element) => { - const invalid = HTMLTagsWithoutChildren.includes(element.tagName); - if (invalid) - console.warn("Hint: If you're trying to attach an image tag to an IMG element, tag a wrapper element instead."); - return !invalid; - }, - }; - - Launched.registerTagFormat("string", InlineTextRenderer); - Launched.registerTagFormat("number", InlineTextRenderer); - Launched.registerTagFormat("link", LinkRenderer); - Launched.registerTagFormat("image", ImageRenderer); - - exports.LaunchedProvider = LaunchedProvider; - exports.default = Launched; - exports.useTag = useTag; - - Object.defineProperty(exports, '__esModule', { value: true }); - -})); -//# sourceMappingURL=bundle.js.map diff --git a/demo/public/scripts/bundle.js.map b/demo/public/scripts/bundle.js.map deleted file mode 100644 index 613186e..0000000 --- a/demo/public/scripts/bundle.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"bundle.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"} \ No newline at end of file diff --git a/demo/public/static-demo.html b/demo/public/static-demo.html index 98804ab..1cab47e 100644 --- a/demo/public/static-demo.html +++ b/demo/public/static-demo.html @@ -11,7 +11,7 @@ - + + + -

Hooray

+

This is a heading.

+

+ Lorem ipsum dolor sit amet consectetur, adipisicing elit. Ea quia + cupiditate architecto perferendis incidunt praesentium neque expedita + ipsam suscipit libero quae dolores veniam quasi, necessitatibus molestiae + dignissimos repellat ullam laborum numquam voluptatem. Officiis eos + dolorem velit a voluptas animi pariatur sint consequatur facilis quaerat? + Magnam asperiores delectus suscipit, ab odit cum voluptas. Corporis, + corrupti! Laudantium explicabo accusamus placeat, sint error, fugit + excepturi nam laboriosam earum blanditiis provident amet, iste modi cum + eveniet dolorum dolores commodi ipsa exercitationem quis eius incidunt + non. Saepe expedita quam ea. Quasi molestiae natus minus, nesciunt ab + exercitationem, voluptate facilis quis obcaecati magni pariatur possimus + molestias, nulla consequuntur voluptas necessitatibus rerum placeat + perferendis dignissimos cupiditate? Assumenda maxime delectus distinctio? + Recusandae hic porro nostrum, ab, maiores voluptatem minima, esse fugit + sunt consequuntur impedit facere cum! +

+

These are some cards.

+ diff --git a/rollup.config.js b/rollup.config.js index fbb2567..4f15798 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -14,6 +14,9 @@ export default { "react-dom/client": "ReactDOM", "react/jsx-runtime": "jsxRuntime", }, + outro: ` + exports["Launched"] = exports.default; + `, }, plugins: [ typescript({ From 91c16a7402b0bdfdf6b0d538b7cdf31655792914 Mon Sep 17 00:00:00 2001 From: MMMJB Date: Wed, 24 Jul 2024 17:21:29 -0400 Subject: [PATCH 3/4] build: set up separate watch script for static demo --- demo/src/index.css | 4 ---- package.json | 4 +++- scripts/build.sh | 39 ++++++++++++++++++++++++++++----------- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/demo/src/index.css b/demo/src/index.css index e846bab..aaf4010 100644 --- a/demo/src/index.css +++ b/demo/src/index.css @@ -758,10 +758,6 @@ video { animation: box-pulse 4s infinite ease-in-out; } -.static{ - position: static; -} - .fixed{ position: fixed; } diff --git a/package.json b/package.json index 4176bc1..6ffc5e3 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,10 @@ "types": "dist/index.d.ts", "scripts": { "build": "rimraf dist && ./scripts/build.sh dist", - "build:demo": "rimraf demo/src/dist && ./scripts/build.sh demo/src/dist", + "build:demo": "rimraf demo/src/dist && ./scripts/build.sh demo/src/dist tsc", + "build:demo-static": "rimraf demo/public/dist && ./scripts/build.sh demo/public/dist umd", "watch": "nodemon --exec \"npm run build:demo\"", + "watch:static": "nodemon --exec \"npm run build:demo-static\"", "test": "vitest run -u --config test/vitest.config.ts", "test:coverage": "vitest run -u --config test/vitest.config.ts --coverage", "test:watch": "vitest -u --config test/vitest.config.ts" diff --git a/scripts/build.sh b/scripts/build.sh index 825983e..250d2fa 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -26,14 +26,31 @@ for file in src/**/*.css; do cp "$source" "$destination" done -echo "----------------------------------------" -echo "Generating UMD bundle..." -echo "----------------------------------------" - -npx rollup -c rollup.config.js || { echo "UMD build failed"; exit 1; } - -echo "----------------------------------------" -echo "Building project..." -echo "----------------------------------------" - -npx tsc --outDir "$DIST" && npx tsc-alias --outDir "$DIST" || { echo "TSC build failed"; exit 1; } +build_mode="$2" + +generate_umd_bundle() { + echo "----------------------------------------" + echo "Generating UMD bundle..." + echo "----------------------------------------" + npx rollup -c rollup.config.js -o "$DIST/bundle.js" || { echo "UMD build failed"; exit 1; } +} + +build_project() { + echo "----------------------------------------" + echo "Building project..." + echo "----------------------------------------" + npx tsc --outDir "$DIST" && npx tsc-alias --outDir "$DIST" || { echo "TSC build failed"; exit 1; } +} + +case "$build_mode" in + umd) + generate_umd_bundle + ;; + tsc) + build_project + ;; + *) + generate_umd_bundle + build_project + ;; +esac \ No newline at end of file From 08badac548d1ff7932caa4f243787eb15d59a128 Mon Sep 17 00:00:00 2001 From: Michael Beck <94383083+MMMJB@users.noreply.github.com> Date: Wed, 24 Jul 2024 17:51:00 -0400 Subject: [PATCH 4/4] chore: update readme --- README.md | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 1793a4f..dafa88a 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,12 @@

- Launched + Launched +

+ Package Size + Version + Downloads +

- Launched is a tool to make website content editable to clients with no codebase access or coding knowledge required. It was created by [Michael Beck](https://linkedin.com/in/michaelbeck0) for the [Launch](https://launchsite.tech) platform. @@ -39,7 +28,11 @@ npm install launched ```html - + + + + + ``` ## Getting started