UNPKG

@silexlabs/grapesjs-data-source

Version:
1,252 lines (1,242 loc) 62.4 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const ref_js_1 = require("lit/directives/ref.js"); const repeat_js_1 = require("lit/directives/repeat.js"); const GraphQL_1 = __importDefault(require("../datasources/GraphQL")); const types_1 = require("../types"); const utils_1 = require("../utils"); const dataSourceRegistry_1 = require("../model/dataSourceRegistry"); const lit_1 = require("lit"); const decorators_js_1 = require("lit/decorators.js"); const COMMON_STYLES = (0, lit_1.css) ` :host { font-family: var(--gjs-main-font); font-size: var(--gjs-font-size); } .ds-field { padding: 10px; } .ds-field > span { display: block; } hr.ds-separator { border: none; border-top: 1px solid var(--ds-button-bg); } .ds-field input, .ds-field select { -webkit-appearance: none; -moz-appearance: none; appearance: none; background-color: var(--gjs-main-dark-color); border: none; box-shadow: none; border-radius: 2px; box-sizing: border-box; padding: 0; position: relative; padding: 10px; color: inherit; width: 100%; } .ds-btn-prim { color:inherit; background-color:var(--gjs-main-light-color); border-radius:2px; padding:3px 6px; padding:var(--gjs-input-padding); cursor:pointer; border:none } .ds-btn-prim:active { background-color:var(--gjs-main-light-color) } .ds-btn-danger { color: var(--gjs-light-color); background-color: transparent; } .ds-btn-danger:hover { color: var(--ds-highlight); } [disabled], [readonly] { font-style: italic; } /* Modal specific styles */ :host(.ds-modal) { max-width: 600px; width: 90vw; } `; exports.default = (editor, options = {}) => { // Settings dialog if (options.settingsEl) { // Get the container element for the UI const settingsEl = (0, utils_1.getElementFromOption)(options.settingsEl, 'options.settingsEl'); const dsSettings = (0, ref_js_1.createRef)(); editor.on(`${types_1.DATA_SOURCE_CHANGED} ${types_1.DATA_SOURCE_ERROR} ${types_1.DATA_SOURCE_READY} ${types_1.DATA_SOURCE_DATA_LOAD_END}`, () => { if (dsSettings.value) { dsSettings.value.dataSources = [...(0, dataSourceRegistry_1.getAllDataSources)()]; dsSettings.value.requestUpdate(); } else { renderSettings(editor, dsSettings, settingsEl); } }); renderSettings(editor, dsSettings, settingsEl); // Add a data source editor.Commands.add(types_1.COMMAND_ADD_DATA_SOURCE, { run() { var _a; (_a = dsSettings.value) === null || _a === void 0 ? void 0 : _a.openDataSourceModal(); }, }); } }; function renderSettings(editor, dsSettings, settingsEl) { (0, lit_1.render)((0, lit_1.html) ` <ds-settings ${(0, ref_js_1.ref)(dsSettings)} .dataSources=${[]} .editor=${editor} @add=${(e) => { const ds = e.detail; const newDS = new GraphQL_1.default(ds); (0, dataSourceRegistry_1.addDataSource)(newDS); }} @add-top=${(e) => { const ds = e.detail; const newDS = new GraphQL_1.default(ds); (0, dataSourceRegistry_1.addDataSource)(newDS); }} @change=${() => { // Refresh the datasources list when a datasource is edited if (dsSettings.value) { const settings = dsSettings.value; settings.dataSources = [...(0, dataSourceRegistry_1.getAllDataSources)()]; settings.requestUpdate(); } }} @delete=${(e) => { const ds = e.detail; (0, dataSourceRegistry_1.removeDataSource)(ds); }} ></ds-settings> `, settingsEl); } /** * <ds-settings> is a web component that renders the settings dialog for the DataSourceEditor plugin */ class SettingsDataSources extends lit_1.LitElement { constructor() { super(); this.updateCounter = 0; this.dataSources = []; this.editor = null; } connectedCallback() { super.connectedCallback(); } openDataSourceModal(dataSource = null) { var _a; if (!this.editor) return; const isEdit = !!dataSource; const title = isEdit ? 'Edit Data Source' : 'Add Data Source'; // Create or use existing data source const ds = dataSource || new GraphQL_1.default((0, utils_1.getDefaultOptions)(this.dataSources.length.toString())); // Create the form element const formElement = document.createElement('ds-settings__data-source'); formElement.dataSource = ds; formElement.isEdit = isEdit; // Handle form events const handleSave = () => { if (isEdit) { // Force new array reference and increment counter to trigger card re-renders this.dataSources = [...this.dataSources]; this.updateCounter++; this.dispatchEvent(new CustomEvent('change', { detail: ds })); } else { this.dispatchEvent(new CustomEvent('add', { detail: ds })); } this.requestUpdate(); this.editor.Modal.close(); }; const handleDelete = () => { this.dispatchEvent(new CustomEvent('delete', { detail: ds })); this.editor.Modal.close(); }; const handleCancel = () => { this.editor.Modal.close(); }; const handleKeyDown = (e) => { if (e.key === 'Escape') { this.editor.Modal.close(); } }; formElement.addEventListener('change', handleSave); formElement.addEventListener('cancel', handleCancel); if (isEdit) { formElement.addEventListener('delete', handleDelete); } // Add ESC key handler document.addEventListener('keydown', handleKeyDown); // Clean up on modal close const modalObserver = new MutationObserver(() => { if (!this.editor.Modal.isOpen()) { document.removeEventListener('keydown', handleKeyDown); modalObserver.disconnect(); } }); const modalParent = (_a = this.editor.Modal.getContentEl()) === null || _a === void 0 ? void 0 : _a.parentElement; if (modalParent) { modalObserver.observe(modalParent, { attributes: true, attributeFilter: ['style', 'class'], }); } // Open the modal this.editor.Modal.open({ title, content: formElement, attributes: { class: 'ds-modal' }, }); } render() { const visibleDataSources = this.dataSources.filter(ds => !ds.hidden); return (0, lit_1.html) ` <section class="ds-settings"> ${visibleDataSources.length === 0 ? (0, lit_1.html) ` <div class="ds-empty-state"> <div class="ds-empty-state__content"> <h3 class="ds-empty-state__title">No Data Sources</h3> <p class="ds-empty-state__description"> Connect to GraphQL APIs to display dynamic data in your website </p> <button type="button" class="ds-btn-prim ds-btn-prim--large" @click=${() => this.openDataSourceModal()} > Add Your First Data Source </button> </div> </div> ` : (0, lit_1.html) ` <div class="ds-data-sources"> ${(0, repeat_js_1.repeat)(visibleDataSources, (ds) => `${ds.id}-${this.updateCounter}`, (ds) => (0, lit_1.html) ` <ds-data-source-card .dataSource=${ds} data-id=${ds.id} @edit=${(e) => this.openDataSourceModal(e.detail)} @test=${(e) => this.testConnection(e.detail)} @delete=${(e) => { this.dispatchEvent(new CustomEvent('delete', { detail: e.detail })); }} ></ds-data-source-card> `)} </div> <div class="ds-add-section"> <button type="button" class="ds-btn-prim" @click=${() => this.openDataSourceModal()} > Add Data Source </button> </div> `} </section> `; } testConnection(dataSource) { var _a; // Find the card component to update its loading state const cardElement = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector(`ds-data-source-card[data-id="${dataSource.id}"]`); if (cardElement) { cardElement.isTestingConnection = true; } // Create a temporary form element to handle the connection test const tempForm = document.createElement('ds-settings__data-source'); tempForm.dataSource = dataSource; // Override the original connectDataSource to handle card updates tempForm.connectDataSource = () => { if (cardElement) { cardElement.isTestingConnection = true; } dataSource.connect().then(() => { if (cardElement) { cardElement.isTestingConnection = false; } this.dispatchEvent(new CustomEvent('change', { detail: dataSource })); this.requestUpdate(); }).catch((err) => { console.error('Data source connection error', { err }); if (cardElement) { cardElement.isTestingConnection = false; } this.requestUpdate(); }); }; tempForm.connectDataSource(); } } SettingsDataSources.styles = [ COMMON_STYLES, (0, lit_1.css) ` .ds-settings { padding: 16px 0; } .ds-empty-state { display: flex; align-items: center; justify-content: center; min-height: 200px; text-align: center; } .ds-empty-state__content { max-width: 300px; } .ds-empty-state__title { margin: 0 0 8px 0; font-size: 18px; color: inherit; } .ds-empty-state__description { margin: 0 0 24px 0; color: var(--gjs-light-color); line-height: 1.4; } .ds-data-sources { margin-bottom: 16px; } .ds-add-section { padding: 16px 0; border-top: 1px solid var(--ds-button-bg); text-align: center; } .ds-btn-prim--large { padding: 12px 24px; font-size: 14px; } `, ]; __decorate([ (0, decorators_js_1.property)({ type: Array }), __metadata("design:type", Array) ], SettingsDataSources.prototype, "dataSources", void 0); __decorate([ (0, decorators_js_1.property)({ type: Object }), __metadata("design:type", Object) ], SettingsDataSources.prototype, "editor", void 0); __decorate([ (0, decorators_js_1.state)(), __metadata("design:type", Number) ], SettingsDataSources.prototype, "updateCounter", void 0); if (!customElements.get('ds-settings')) { customElements.define('ds-settings', SettingsDataSources); } class SettingsDataSource extends lit_1.LitElement { // private unknownIcon = html`<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="12" r="2"/></svg>` constructor() { super(); this.isEdit = false; // State for the creation flow this.errorMessage = ''; this.connected = false; this.isLoading = false; this.creationPhase = 'config'; // config = initial form, types = type selection this.availableTypes = []; // List of types (name + kind) from lightweight query this.selectedTypes = new Set(); // Currently selected types this.typeSearchQuery = ''; // Search/filter for type list this.queryTypeName = 'Query'; // The query type name from schema introspection this.loadingIcon = (0, lit_1.html) `<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="12" r="3"><animate attributeName="r" values="3;8;3" dur="1s" repeatCount="indefinite"/><animate attributeName="opacity" values="1;0.3;1" dur="1s" repeatCount="indefinite"/></circle></svg>`; this.connectedIcon = (0, lit_1.html) `<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>`; this.dataSource = null; } connectedCallback() { super.connectedCallback(); } /** * Phase 1: Fetch lightweight type names from the GraphQL endpoint */ fetchTypeNames() { return __awaiter(this, void 0, void 0, function* () { if (!this.dataSource || !(this.dataSource instanceof GraphQL_1.default)) { this.errorMessage = 'Invalid data source'; return; } this.isLoading = true; this.errorMessage = ''; this.requestUpdate(); try { const ds = this.dataSource; const result = yield ds.fetchTypeNames(); // Store the queryType name from the schema this.queryTypeName = result.queryTypeName; // Sort types by name this.availableTypes = result.types.sort((a, b) => a.name.localeCompare(b.name)); // For existing datasources with disabledTypes, compute selected = all - disabled // Otherwise, apply backend-specific default selection if (this.isEdit && ds.disabledTypes && ds.disabledTypes.length > 0) { const allTypeNames = this.availableTypes.map(t => t.name); const selectedNames = allTypeNames.filter(name => !ds.disabledTypes.includes(name)); this.selectedTypes = new Set(selectedNames); } else { const backendType = ds.backendType || 'generic'; const defaultEnabled = GraphQL_1.default.getDefaultEnabledTypes(backendType, this.availableTypes, this.queryTypeName); this.selectedTypes = new Set(defaultEnabled); } // Move to type selection phase this.creationPhase = 'types'; this.isLoading = false; this.requestUpdate(); } catch (e) { console.error('[Settings] Failed to fetch type names:', e); this.errorMessage = e.message; this.isLoading = false; this.requestUpdate(); } }); } /** * Check if a type is required (cannot be blacklisted) * The queryType and all SCALAR types are required */ isRequiredType(typeName) { if (typeName === this.queryTypeName) return true; const type = this.availableTypes.find(t => t.name === typeName); return (type === null || type === void 0 ? void 0 : type.kind) === 'SCALAR'; } /** * Toggle a type's selected state */ toggleType(typeName) { // Query and SCALAR types cannot be unchecked - they're always required if (this.isRequiredType(typeName)) return; if (this.selectedTypes.has(typeName)) { this.selectedTypes.delete(typeName); } else { this.selectedTypes.add(typeName); } this.selectedTypes = new Set(this.selectedTypes); // Trigger reactivity this.requestUpdate(); } /** * Select all types */ selectAllTypes() { this.selectedTypes = new Set(this.availableTypes.map(t => t.name)); this.requestUpdate(); } /** * Deselect all types (except queryType and SCALAR types which are always required) */ deselectAllTypes() { // Always keep queryType and SCALAR types selected const requiredTypes = this.availableTypes .filter(t => t.name === this.queryTypeName || t.kind === 'SCALAR') .map(t => t.name); this.selectedTypes = new Set(requiredTypes); this.requestUpdate(); } /** * Reset types to default selection based on backend type */ resetToDefaults() { const isGraphQL = this.dataSource instanceof GraphQL_1.default; if (!isGraphQL) return; const backendType = this.dataSource.backendType || 'generic'; const defaultEnabled = GraphQL_1.default.getDefaultEnabledTypes(backendType, this.availableTypes, this.queryTypeName); this.selectedTypes = new Set(defaultEnabled); this.requestUpdate(); } /** * Get filtered types based on search query * Selected types are always shown first */ getFilteredTypes() { let types = this.availableTypes; if (this.typeSearchQuery) { const query = this.typeSearchQuery.toLowerCase(); types = types.filter(t => t.name.toLowerCase().includes(query)); } // Sort: selected types first, then alphabetically within each group return types.sort((a, b) => { const aSelected = this.selectedTypes.has(a.name); const bSelected = this.selectedTypes.has(b.name); if (aSelected && !bSelected) return -1; if (!aSelected && bSelected) return 1; return a.name.localeCompare(b.name); }); } /** * Go back to config phase */ goBackToConfig() { this.creationPhase = 'config'; this.availableTypes = []; this.selectedTypes = new Set(); this.queryTypeName = 'Query'; this.errorMessage = ''; this.requestUpdate(); } /** * Handle backend type change - updates selected types based on new backend defaults */ onBackendTypeChange(newBackendType) { if (!this.dataSource || !(this.dataSource instanceof GraphQL_1.default)) return; const ds = this.dataSource; ds.backendType = newBackendType; // Apply new default selection based on backend type const defaultEnabled = GraphQL_1.default.getDefaultEnabledTypes(newBackendType, this.availableTypes, this.queryTypeName); this.selectedTypes = new Set(defaultEnabled); this.requestUpdate(); } connectDataSource() { if (!this.dataSource) throw new Error('No data source provided'); // Compute disabledTypes from selection (blacklist approach) if (this.dataSource instanceof GraphQL_1.default && this.availableTypes.length > 0) { const ds = this.dataSource; const allTypeNames = this.availableTypes.map(t => t.name); const disabledTypes = allTypeNames.filter(name => !this.selectedTypes.has(name)); // Set disabled types for persistence (only blacklist, no whitelist) ds.disabledTypes = disabledTypes; } this.isLoading = true; this.errorMessage = ''; this.requestUpdate(); this.dataSource.connect().then(() => { this.dispatchEvent(new CustomEvent('change')); this.errorMessage = ''; this.connected = true; this.isLoading = false; this.requestUpdate(); }).catch((err) => { console.error('Data source connection error', { err }); this.errorMessage = err.message; this.connected = false; this.isLoading = false; this.requestUpdate(); }); } /** * Render the configuration form (Phase 1) */ renderConfigPhase() { if (!this.dataSource) throw new Error('No data source provided'); const dsHeaders = (0, ref_js_1.createRef)(); return (0, lit_1.html) ` <h3 class="ds-property__title"> ${this.dataSource.label || 'Unnamed'} <small>${this.dataSource.readonly !== false ? ' (Read-only)' : ''}</small> </h3> <div class="ds-property__wrapper ds-property__wrapper--horiz"> <label class="ds-field"> <span>Label</span> <input type="text" name="label" value=${this.dataSource.label} @input=${(e) => { if (this.dataSource) this.dataSource.label = e.target.value; this.requestUpdate(); }} ?readonly=${this.dataSource.readonly !== false} /> </label> <label class="ds-field ds-field--large"> <span>URL</span> <input type="url" name="url" value=${this.dataSource.url} @input=${(e) => { if (this.dataSource) { this.dataSource.url = e.target.value; this.requestUpdate(); } }} ?readonly=${this.dataSource.readonly !== false} /> </label> <!-- ID field hidden - not needed by users, auto-generated --> <label class="ds-field"> <span>Method</span> <select name="method" @change=${(e) => { if (this.dataSource) this.dataSource.method = e.target.value; }} ?readonly=${this.dataSource.readonly !== false} ?disabled=${this.dataSource.readonly !== false} > <option value="POST" ?selected=${this.dataSource.method === 'POST'}>POST</option> <option value="GET" ?selected=${this.dataSource.method === 'GET'}>GET</option> </select> </label> </div> <details class="ds-advanced-details"> <summary class="ds-advanced-summary">HTTP Headers</summary> <div class="ds-advanced-content"> <ds-settings__headers ${(0, ref_js_1.ref)(dsHeaders)} .headers=${this.dataSource.headers} @change=${() => { var _a, _b; if (this.dataSource) this.dataSource.headers = ((_a = dsHeaders.value) === null || _a === void 0 ? void 0 : _a.headers) || {}; (_b = dsHeaders.value) === null || _b === void 0 ? void 0 : _b.requestUpdate(); }} ?readonly=${this.dataSource.readonly !== false} ></ds-settings__headers> </div> </details> <div class="ds-field"> <div class="ds-status-section"> ${this.isLoading ? (0, lit_1.html) `<div class="ds-status-item ds-status-loading">${this.loadingIcon} Fetching types...</div>` : this.errorMessage ? (0, lit_1.html) `<div class="ds-status-item ds-status-error">Error: ${this.errorMessage}</div>` : ''} </div> </div> <div class="ds-field ds-actions"> <div class="ds-actions-right"> <button type="button" class="ds-btn-prim" @click=${() => this.dispatchEvent(new CustomEvent('cancel'))} >Cancel</button> ${this.isEdit ? (0, lit_1.html) ` <button type="button" class="ds-btn-prim" ?disabled=${this.isLoading || !this.dataSource.url} @click=${() => this.fetchTypeNames()} >${this.isLoading ? 'Loading...' : 'Options'}</button> <button type="submit" class="ds-btn-prim" ?disabled=${this.isLoading || !this.dataSource.url} >${this.isLoading ? 'Applying...' : 'Apply'}</button> ` : (0, lit_1.html) ` <button type="submit" class="ds-btn-prim" ?disabled=${this.isLoading || !this.dataSource.url} >${this.isLoading ? 'Fetching...' : 'Next'}</button> `} </div> </div> </div> `; } /** * Render the type selection phase (Phase 2) */ renderTypesPhase() { const filteredTypes = this.getFilteredTypes(); const selectedCount = this.selectedTypes.size; const totalCount = this.availableTypes.length; const isGraphQL = this.dataSource instanceof GraphQL_1.default; const backendType = isGraphQL ? this.dataSource.backendType : 'generic'; return (0, lit_1.html) ` <h3 class="ds-property__title">Data Source Options</h3> <p class="ds-help-text">Select your backend type. Any GraphQL API is a supported, choose "GraphQL" for a generic connector.</p> <div class="ds-backend-selector"> <fieldset class="ds-backend-options"> <label class="ds-backend-option ${backendType === 'generic' ? 'ds-backend-option--selected' : ''}"> <input type="radio" name="backendType" value="generic" ?checked=${backendType === 'generic'} @change=${() => this.onBackendTypeChange('generic')} /> <svg viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg"> <path fill="#E535AB" d="M57.468 302.66l-14.376-8.3 160.15-277.38 14.376 8.3z"/> <path fill="#E535AB" d="M39.8 272.2h320.3v16.6H39.8z"/> <path fill="#E535AB" d="M206.348 374.026l-160.21-92.5 8.3-14.376 160.21 92.5zM345.522 132.947l-160.21-92.5 8.3-14.376 160.21 92.5z"/> <path fill="#E535AB" d="M54.482 132.883l-8.3-14.375 160.21-92.5 8.3 14.376z"/> <path fill="#E535AB" d="M342.568 302.663l-160.15-277.38 14.376-8.3 160.15 277.38zM52.5 107.5h16.6v185H52.5zM330.9 107.5h16.6v185h-16.6z"/> <path fill="#E535AB" d="M203.522 367l-7.25-12.558 139.34-80.45 7.25 12.557z"/> <path fill="#E535AB" d="M369.5 297.9c-9.6 16.7-31 22.4-47.7 12.8-16.7-9.6-22.4-31-12.8-47.7 9.6-16.7 31-22.4 47.7-12.8 16.8 9.7 22.5 31 12.8 47.7M90.9 137c-9.6 16.7-31 22.4-47.7 12.8-16.7-9.6-22.4-31-12.8-47.7 9.6-16.7 31-22.4 47.7-12.8 16.7 9.7 22.4 31 12.8 47.7M30.5 297.9c-9.6-16.7-3.9-38 12.8-47.7 16.7-9.6 38-3.9 47.7 12.8 9.6 16.7 3.9 38-12.8 47.7-16.8 9.6-38.1 3.9-47.7-12.8M309.1 137c-9.6-16.7-3.9-38 12.8-47.7 16.7-9.6 38-3.9 47.7 12.8 9.6 16.7 3.9 38-12.8 47.7-16.7 9.6-38.1 3.9-47.7-12.8M200 395.8c-19.3 0-34.9-15.6-34.9-34.9 0-19.3 15.6-34.9 34.9-34.9 19.3 0 34.9 15.6 34.9 34.9 0 19.2-15.6 34.9-34.9 34.9M200 74c-19.3 0-34.9-15.6-34.9-34.9 0-19.3 15.6-34.9 34.9-34.9 19.3 0 34.9 15.6 34.9 34.9 0 19.3-15.6 34.9-34.9 34.9"/> </svg> <span>GraphQL</span> </label> <label class="ds-backend-option ds-backend-option--beta ${backendType === 'wordpress' ? 'ds-backend-option--selected' : ''}"> <input type="radio" name="backendType" value="wordpress" ?checked=${backendType === 'wordpress'} @change=${() => this.onBackendTypeChange('wordpress')} /> <svg viewBox="0 0 122.52 122.523" xmlns="http://www.w3.org/2000/svg"> <g fill="#21759b"> <path d="m8.708 61.26c0 20.802 12.089 38.779 29.619 47.298l-25.069-68.686c-2.916 6.536-4.55 13.769-4.55 21.388z"/> <path d="m96.74 58.608c0-6.495-2.333-10.993-4.334-14.494-2.664-4.329-5.161-7.995-5.161-12.324 0-4.831 3.664-9.328 8.825-9.328.233 0 .454.029.681.042-9.35-8.566-21.807-13.796-35.489-13.796-18.36 0-34.513 9.42-43.91 23.688 1.233.037 2.395.063 3.382.063 5.497 0 14.006-.667 14.006-.667 2.833-.167 3.167 3.994.337 4.329 0 0-2.847.335-6.015.501l19.138 56.925 11.501-34.493-8.188-22.434c-2.83-.166-5.511-.501-5.511-.501-2.832-.166-2.5-4.496.332-4.329 0 0 8.679.667 13.843.667 5.496 0 14.006-.667 14.006-.667 2.835-.167 3.168 3.994.337 4.329 0 0-2.853.335-6.015.501l18.992 56.494 5.242-17.517c2.272-7.269 4.001-12.49 4.001-16.989z"/> <path d="m62.184 65.857-15.768 45.819c4.708 1.384 9.687 2.141 14.846 2.141 6.12 0 11.989-1.058 17.452-2.979-.14-.225-.269-.464-.374-.724z"/> <path d="m107.376 36.046c.226 1.674.354 3.471.354 5.404 0 5.333-.996 11.328-3.996 18.824l-16.053 46.413c15.624-9.111 26.133-26.038 26.133-45.426.001-9.137-2.333-17.729-6.438-25.215z"/> <path d="m61.262 0c-33.779 0-61.262 27.481-61.262 61.26 0 33.783 27.483 61.263 61.262 61.263 33.778 0 61.265-27.48 61.265-61.263-.001-33.779-27.487-61.26-61.265-61.26zm0 119.715c-32.23 0-58.453-26.223-58.453-58.455 0-32.23 26.222-58.451 58.453-58.451 32.229 0 58.45 26.221 58.45 58.451 0 32.232-26.221 58.455-58.45 58.455z"/> </g> </svg> <span>WordPress</span> </label> <label class="ds-backend-option ds-backend-option--beta ${backendType === 'gitlab' ? 'ds-backend-option--selected' : ''}"> <input type="radio" name="backendType" value="gitlab" ?checked=${backendType === 'gitlab'} @change=${() => this.onBackendTypeChange('gitlab')} /> <svg viewBox="0 0 380 380" xmlns="http://www.w3.org/2000/svg"> <path fill="#e24329" d="M190 362.6 256.3 158.8H123.7z"/> <path fill="#fc6d26" d="M190 362.6 123.7 158.8H15.3z"/> <path fill="#fca326" d="m15.3 158.8-13.5 41.6c-1.2 3.8.1 7.9 3.3 10.3L190 362.6z"/> <path fill="#e24329" d="M15.3 158.8h108.4L79.8 24.1c-1.4-4.4-7.6-4.4-9 0z"/> <path fill="#fc6d26" d="m190 362.6 66.3-203.8h108.4z"/> <path fill="#fca326" d="m364.7 158.8 13.5 41.6c1.2 3.8-.1 7.9-3.3 10.3L190 362.6z"/> <path fill="#e24329" d="M364.7 158.8H256.3l43.9-134.7c1.4-4.4 7.6-4.4 9 0z"/> </svg> <span>GitLab</span> </label> <label class="ds-backend-option ds-backend-option--beta ${backendType === 'strapi' ? 'ds-backend-option--selected' : ''}"> <input type="radio" name="backendType" value="strapi" ?checked=${backendType === 'strapi'} @change=${() => this.onBackendTypeChange('strapi')} /> <svg viewBox=".24262095 .26549587 243.32256626 243.58072911" xmlns="http://www.w3.org/2000/svg"> <g fill="#8e75ff" fill-rule="evenodd"> <path d="m161.893 165.833v-78.73a5.077 5.077 0 0 0 -5.077-5.076h-78.638v-81.267h159.815a5.077 5.077 0 0 1 5.078 5.077v159.996z"/> <path d="m78.178.76v81.267h-75.054a2.539 2.539 0 0 1 -1.796-4.333zm83.715 240.206v-75.133h81.178l-76.844 76.927a2.539 2.539 0 0 1 -4.334-1.794zm-83.715-158.939h81.176a2.539 2.539 0 0 1 2.539 2.538v81.268h-78.638a5.077 5.077 0 0 1 -5.077-5.077z" opacity=".405"/> </g> </svg> <span>Strapi</span> </label> <label class="ds-backend-option ds-backend-option--beta ${backendType === 'supabase' ? 'ds-backend-option--selected' : ''}"> <input type="radio" name="backendType" value="supabase" ?checked=${backendType === 'supabase'} @change=${() => this.onBackendTypeChange('supabase')} /> <svg viewBox="0 0 109 113" xmlns="http://www.w3.org/2000/svg"> <path fill="url(#supabase-a)" d="M63.7 110.3c-2.5 3.1-7.6 1.4-7.7-2.6l-1.5-66.2h50.2c9.1 0 14.1 10.5 8.4 17.6L63.7 110.3Z"/> <path fill="url(#supabase-b)" fill-opacity=".2" d="M63.7 110.3c-2.5 3.1-7.6 1.4-7.7-2.6l-1.5-66.2h50.2c9.1 0 14.1 10.5 8.4 17.6L63.7 110.3Z"/> <path fill="#3ECF8E" d="M45.3 2.7c2.5-3.1 7.6-1.4 7.7 2.6l.7 66.2H4.4c-9.1 0-14-10.5-8.3-17.6L45.3 2.7Z"/> <defs> <linearGradient id="supabase-a" x1="53.97" x2="94.12" y1="54.97" y2="71.95" gradientUnits="userSpaceOnUse"> <stop stop-color="#249361"/><stop offset="1" stop-color="#3ECF8E"/> </linearGradient> <linearGradient id="supabase-b" x1="36.16" x2="54.48" y1="30.58" y2="65.72" gradientUnits="userSpaceOnUse"> <stop/><stop offset="1" stop-opacity="0"/> </linearGradient> </defs> </svg> <span>Supabase</span> </label> </fieldset> </div> <!-- Advanced: Manual type selection --> <details class="ds-advanced-details"> <summary class="ds-advanced-summary"> Show / hide fields in the expressions <span class="ds-types-count-inline">(${selectedCount}/${totalCount})</span> </summary> <div class="ds-advanced-content"> <p class="ds-help-text"><br>Uncheck types you don't want to see in your expressions.</p> <div class="ds-types-header"> <input type="text" class="ds-types-search" placeholder="Search types..." .value=${this.typeSearchQuery} @input=${(e) => { this.typeSearchQuery = e.target.value; this.requestUpdate(); }} /> <div class="ds-types-actions"> <button type="button" class="ds-btn-prim" @click=${() => this.selectAllTypes()}>Select All</button> <button type="button" class="ds-btn-prim" @click=${() => this.deselectAllTypes()}>Deselect All</button> <button type="button" class="ds-btn-prim" @click=${() => this.resetToDefaults()}>Reset to Defaults</button> </div> </div> <div class="ds-types-list"> ${filteredTypes.map(type => { const isRequired = this.isRequiredType(type.name); return (0, lit_1.html) ` <div class="ds-type-item" @click=${() => this.toggleType(type.name)}> <input type="checkbox" .checked=${this.selectedTypes.has(type.name)} ?disabled=${isRequired} @click=${(e) => e.stopPropagation()} @change=${() => this.toggleType(type.name)} /> <label>${type.name}</label> <span class="ds-type-kind ds-type-kind--${type.kind.toLowerCase()}">${type.kind}</span> </div> `; })} ${filteredTypes.length === 0 ? (0, lit_1.html) ` <div style="padding: 20px; text-align: center; color: var(--gjs-light-color);"> No types match "${this.typeSearchQuery}" </div> ` : ''} </div> <div class="ds-types-count"> ${selectedCount} of ${totalCount} types selected ${this.typeSearchQuery ? ` (showing ${filteredTypes.length} matching "${this.typeSearchQuery}")` : ''} </div> </div> </details> <div class="ds-field"> <div class="ds-status-section"> ${this.isLoading ? (0, lit_1.html) `<div class="ds-status-item ds-status-loading">${this.loadingIcon} Connecting...</div>` : this.errorMessage ? (0, lit_1.html) `<div class="ds-status-item ds-status-error">Error: ${this.errorMessage}</div>` : this.connected ? (0, lit_1.html) `<div class="ds-status-item ds-status-success">${this.connectedIcon} Connection successful</div>` : ''} </div> </div> <div class="ds-field ds-actions"> <div class="ds-actions-left"> <button type="button" class="ds-btn-prim" @click=${() => this.goBackToConfig()} ?disabled=${this.isLoading} >Back</button> </div> <div class="ds-actions-right"> <button type="button" class="ds-btn-prim" @click=${() => this.dispatchEvent(new CustomEvent('cancel'))} >Cancel</button> <button type="submit" class="ds-btn-prim" ?disabled=${this.isLoading || this.selectedTypes.size === 0} >${this.isLoading ? 'Connecting...' : 'Apply'}</button> </div> </div> `; } render() { if (!this.dataSource) throw new Error('No data source provided'); return (0, lit_1.html) ` <form ?readonly=${this.dataSource.readonly !== false} @submit=${(e) => { e.preventDefault(); e.stopImmediatePropagation(); // Handle form submission based on current phase if (this.creationPhase === 'config') { if (this.isEdit) { this.connectDataSource(); } else { this.fetchTypeNames(); } } else { this.connectDataSource(); } }} > ${this.creationPhase === 'config' ? this.renderConfigPhase() : this.renderTypesPhase()} </form> `; } } SettingsDataSource.styles = [ (0, lit_1.css) ` form { display: flex; flex-direction: column; align-items: stretch; } .ds-field--large { flex: 1 1 auto; } .ds-property__wrapper { display: flex; flex-direction: column; width: 100%; flex-wrap: wrap; } .ds-property__wrapper--horiz { flex-direction: row; } .ds-property__wrapper--vert { flex-direction: column; } .ds-status-section { margin: 10px 0; } .ds-status-item { display: flex; align-items: center; gap: 8px; padding: 8px 12px; border-radius: 4px; font-size: 14px; } .ds-status-loading { background-color: var(--gjs-main-dark-color); color: var(--ds-highlight); } .ds-status-success { background-color: var(--gjs-main-dark-color); color: var(--ds-primary); } .ds-status-error { background-color: var(--gjs-main-dark-color); color: var(--ds-highlight); } .ds-actions { display: flex; gap: 10px; justify-content: space-between; align-items: center; margin-top: 15px; } .ds-actions-left { display: flex; gap: 10px; } .ds-actions-right { display: flex; gap: 10px; margin-left: auto; } .ds-no-resize { flex: 0 0 auto; } button:disabled { opacity: 0.6; cursor: not-allowed; } /* Backend selector styles */ .ds-backend-selector { margin-bottom: 15px; } .ds-backend-options { display: flex; gap: 10px; border: none; padding: 0; margin: 0; } .ds-backend-option { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 12px 16px; border: 2px solid var(--gjs-main-dark-color); border-radius: 8px; cursor: pointer; transition: all 0.2s ease; background: var(--gjs-main-dark-color); min-width: 80px; } .ds-backend-option:hover { border-color: var(--gjs-light-color); } .ds-backend-option--selected { border-color: var(--ds-primary); background: rgba(var(--ds-primary-rgb, 76, 175, 80), 0.1); } .ds-backend-option input[type="radio"] { position: absolute; opacity: 0; pointer-events: none; } .ds-backend-option svg { width: 40px; height: 40px; } .ds-backend-option span { font-size: 11px; color: var(--gjs-light-color); text-align: center; } .ds-backend-option--beta { position: relative; } .ds-backend-option--beta::after { content: 'beta'; position: absolute; top: 4px; right: 4px; padding: 1px 4px; padding-top: 3px; font-size: smaller; font-weight: bold; text-transform: uppercase; background: var(--ds-primary); color: var(--gjs-secondary-color); border-radius: 3px; line-height: 1.2; } /* Inline backend selector (compact for config phase) */ .ds-backend-selector--inline { display: flex; gap: 8px; margin-top: 8px; } .ds-backend-option--compact { flex-direction: row; padding: 8px 14px; min-width: auto; gap: 8px; } .ds-backend-option--compact svg { width: 20px; height: 20px; } .ds-backend-option--compact span { font-size: 13px; } /* Advanced options accordion */ .ds-advanced-details { margin: 15px 0; border: 1px solid var(--gjs-main-dark-color); border-radius: 6px; background: var(--gjs-main-dark-color); } .ds-advanced-summary { padding: 12px 15px; cursor: pointer; font-size: 13px; color: var(--gjs-light-color); user-select: none; display: flex; align-items: center; gap: 8px; } .ds-advanced-summary:hover { color: inherit; } .ds-advanced-summary::before { content: '▶'; font-size: 10px; transition: transform 0.2s; } .ds-advanced-details[open] .ds-advanced-summary::before { transform: rotate(90deg); } .ds-advanced-content { padding: 0 15px 15px; border-top: 1px solid var(--gjs-main-light-color); } .ds-hint { display: block; margin-top: 5px; font-size: 11px; color: var(--gjs-light-color); } .ds-help-text { margin: 0 0 10px; font-size: 11px; color: var(--gjs-light-color); font-style: italic; } .ds-types-count-inline { font-size: 12px; color: var(--gjs-light-color); margin-left: auto; } /* Type selection styles */ .ds-types-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; flex-wrap: wrap; gap: 10px; } .ds-types-search { flex: 1; min-width: 150px; } .ds-types-actions { display: flex; gap: 5px; } .ds-types-actions button { padding: 4px 8px; font-size: 12px; } .ds-types-list { max-height: 300px; overflow-y: auto; border: 1px solid var(--gjs-main-dark-color); border-radius: 4px; padding: 8px; background-color: var(--gjs-main-dark-color); } .ds-type-item { display: flex; align-items: center; gap: 8px; padding: 4px 8px; border-radius: 3px; cursor: pointer; transition: background-color 0.15s; } .ds-type-item:hover { background-color: var(--gjs-main-light-color); } .ds-type-item input[type="checkbox"] { width: 16px; height: 16px; cursor: pointer; } .ds-type-item label { cursor: pointer; flex: 1; font-family: monospace; font-size: 13px; } .ds-type-kind { font-size: 10px; padding: 2px 5px; border-radius: 3px; background-color: var(--gjs-main-light-color); color: var(--gjs-light-color); text-transform: lowercase; } .ds-type-kind--scalar { background-color: rgba(100, 200, 100, 0.2); color: #4caf50; } .ds-type-kind--object { background-color: rgba(100, 150, 255, 0.2); color: #2196f3; } .ds-type-kind--interface { background-color: rgba(200, 150, 255, 0.2); color: #9c27b0; } .ds-type-kind--enum { background-color: rgba(255, 200, 100, 0.2); color: #ff9800; } .ds-type-kind--union { background-color: rgba(255, 150, 150, 0.2); color: #f44336; } .ds-type-kind--input_object { background-color: rgba(150, 200, 200, 0.2); color: #00bcd4; } .ds-types-count { font-size: 12px; color: var(--gjs-light-color); margin-top: 8px; } `, COMMON_STYLES, ]; __decorate([ (0, decorators_js_1.property)({ type: Object }), __metadata("design:type", Object) ], SettingsDataSource.prototype, "dataSource", void 0); __decorate([ (0, decorators_js_1.property)({ type: Boolean }), __metadata("design:type", Boolean) ], SettingsDataSource.prototype, "isEdit", void 0); __decorate([ (0, decorators_js_1.state)(), __metadata("design:type", String) ], SettingsDataSource.prototype, "errorMessage", void 0); __decorate([ (0, decorators_js_1.state)(), __metadata("design:type", Boolean) ], SettingsDataSource.prototype, "connected", void 0); __decorate([ (0, decorators_js_1.state)(), __metadata("design:type", Boolean) ], SettingsDataSource.prototype, "isLoading", void 0); __decorate([ (0, decorators_js_1.state)(), __metadata("design:type", String) ], SettingsDataSource.prototype, "creationPhase", void 0); __decorate([ (0, decorators_js_1.state)(), __metadata("design:type", Array) ], SettingsDataSource.prototype, "availableTypes", void 0); __decorate([ (0, decorators_js_1.state)(), __metadata("design:type", Set) ], SettingsDataSource.prototype, "selectedTypes", void 0); __decorate([ (0, decorators_js_1.state)(), __metadata("design:type", String) ], SettingsDataSource.prototype, "typeSearchQuery", void 0); __decorate([ (0, decorators_js_1.state)(), __metadata("design:type", String) ], SettingsDataSource.prototype, "queryTypeName", void 0); if (!customElements.get('ds-settings__data-source')) { customElements.define('ds-settings__data-source', SettingsDataSource); } /** * Compact card component for displaying data source summary */ class DataSourceCard extends lit_1.LitElement { // private connectedIcon = html`<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>` // private errorIcon = html`<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/></svg>` constructor() { super(); this.isTestingConnection = false; this.loadingIcon = (0, lit_1.html) `<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="12" r="3"><animate attributeName="r" values="3;8;3" dur="1s" repeatCount="indefinite"/><animate attributeName="opacity" values="1;0.3;1" dur="1s" repeatCount="indefinite"/></circle></svg>`; this.graphqlLogo = (0, lit_1.html) `<svg class="ds-card__logo" viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg"><path fill="#E535AB" d="M57.468 302.66l-14.376-8.3 160.15-277.38 14.376 8.3z"/><path fill="#E535AB" d="M39.8 272.2h320.3v16.6H39.8z"/><path fill="#E535AB" d="M206.348 374.026l-160.21-92.5 8.3-14.376 160.21 92.5zM345.522 132.947l-160.21-92.5 8.3-14.376 160.21 92.5z"/><path fill="#E535AB" d="M54.482 132.883l-8.3-14.375 160.21-92.5 8.3 14.376z"/><path fill="#E535AB" d="M342.568 302.663l-160.15-277.38 14.376-8.3 160.15 277.38zM52.5 107.5h16.6v185H52.5zM330.9 107.5h16.6v185h-16.6z"/><path fill="#E535AB" d="M203.522 367l-7.25-12.558 139.34-80.45 7.25 12.557z"/><path fill="#E535AB" d="M369.5 297.9c-9.6 16.7-31 22.4-47.7 12.8-16.7-9.6-22.4-31-12.8-47.7 9.6-16.7 31-22.4 47.7-12.8 16.8 9.7 22.5 31 12.8 47.7M90.9 137c-9.6 16.7-31 22.4-47.7 12.8-16.7-9.6-22.4-31-12.8-47.7 9.6-16.7 31-22.4 47.7-12.8 16.7 9.7 22.4 31 12.8 47.7M30.5 297.9c-9.6-16.7-3.9-38 12.8-47.7 16.7-9.6 38-3.9 47.7 12.8 9.6 16.7 3.9 38-12.8 47.7-16.8 9.6-38.1 3.9-47.7-12.8M309.1 137c-9.6-16.7-3.9-38 12.8-47.7 16.7-9.6 38-3.9 47.7 12.8 9.6 16.7 3.9 38-12.8 47.7-16.7 9.6-38.1 3.9-47.7-12.8M200 395.8c-19.3 0-34.9-15.6-34.9-34.9 0-19.3 15.6-34.9 34.9-34.9 19.3 0 34.9 15.6 34.9 34.9 0 19.2-15.6 34.9-34.9 34.9M200 74c-19.3 0-34.9-15.6-34.9-34.9 0-19.3 15.6-34.9 34.9-34.9 19.3 0 34.9 15.6 34.9 34.9 0 19.3-15.6 34.9-34.9 34.9"/></svg>`; this.wordpressLogo = (0, lit_1.html) `<svg class="ds-card__logo" viewBox="0 0 122.52 122.523" xmlns="http://www.w3.org/2000/svg"><g fill="#21759b"><path d="m8.708 61.26c0 20.802 12.089 38.779 29.619 47.298l-25.069-68.686c-2.916 6.5