mirror of
https://github.com/fluxscape/fluxscape.git
synced 2026-01-11 14:52:54 +01:00
Compare commits
14 Commits
v1.1.0
...
feature/sa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec2ea33ffd | ||
|
|
5dbb11bac8 | ||
|
|
d80870e835 | ||
|
|
a98e381f8c | ||
|
|
72aec29e27 | ||
|
|
2eb18acfef | ||
|
|
e1a1b31213 | ||
|
|
5febc490b4 | ||
|
|
48541347f0 | ||
|
|
cc79ea5f7e | ||
|
|
46f6cb2da9 | ||
|
|
34c3d07112 | ||
|
|
d85dce8d02 | ||
|
|
89ed2d602f |
@@ -0,0 +1,142 @@
|
|||||||
|
import React, { createContext, useContext, useState, useEffect } from 'react';
|
||||||
|
|
||||||
|
import { type ComponentModel } from '@noodl-models/componentmodel';
|
||||||
|
import { type NodeGraphNode } from '@noodl-models/nodegraphmodel';
|
||||||
|
import { type NodeLibraryNodeType } from '@noodl-models/nodelibrary';
|
||||||
|
import { type Slot } from '@noodl-core-ui/types/global';
|
||||||
|
import { ProjectModel } from '@noodl-models/projectmodel';
|
||||||
|
import { EventDispatcher } from '../../../../shared/utils/EventDispatcher';
|
||||||
|
|
||||||
|
export type NodeReference = {
|
||||||
|
type: NodeLibraryNodeType | undefined;
|
||||||
|
displayName: string;
|
||||||
|
referenaces: {
|
||||||
|
displayName: string;
|
||||||
|
node?: NodeGraphNode;
|
||||||
|
component: ComponentModel;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface NodeReferencesContext {
|
||||||
|
nodeReferences: NodeReference[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const NodeReferencesContext = createContext<NodeReferencesContext>({
|
||||||
|
nodeReferences: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Since all the editor code is not written in React we need a way to be able to
|
||||||
|
// access this information outside of React too.
|
||||||
|
let HACK_nodeReferences: NodeReference[] = [];
|
||||||
|
export function HACK_findNodeReference(componentName: string): NodeReference | undefined {
|
||||||
|
return HACK_nodeReferences.find(x => x.type?.fullName === componentName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface NodeReferencesContextProps {
|
||||||
|
children: Slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NodeReferencesContextProvider({ children }: NodeReferencesContextProps) {
|
||||||
|
const [group] = useState({});
|
||||||
|
const [nodeReferences, setNodeReferences] = useState<NodeReference[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function updateIndex() {
|
||||||
|
const types: { [key: string]: NodeReference['type'] } = {};
|
||||||
|
const references = new Map<string, NodeReference['referenaces']>();
|
||||||
|
|
||||||
|
function handleComponent(component: ComponentModel) {
|
||||||
|
component.forEachNode((node: NodeGraphNode) => {
|
||||||
|
const name = node.type.name;
|
||||||
|
|
||||||
|
// Add the reference
|
||||||
|
references.set(name, [
|
||||||
|
...(references.get(name) || []),
|
||||||
|
{
|
||||||
|
displayName: component.displayName || component.name,
|
||||||
|
node,
|
||||||
|
component
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Repeater
|
||||||
|
if (name === 'For Each' && node.parameters.template) {
|
||||||
|
const templateComponent = ProjectModel.instance.getComponentWithName(node.parameters.template);
|
||||||
|
|
||||||
|
if (templateComponent) {
|
||||||
|
references.set(templateComponent.fullName, [
|
||||||
|
...(references.get(templateComponent.fullName) || []),
|
||||||
|
{
|
||||||
|
displayName: component.displayName || component.name,
|
||||||
|
node,
|
||||||
|
component
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
handleComponent(templateComponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add some metadata for this node if we dont have it yet.
|
||||||
|
if (!types[name]) {
|
||||||
|
types[name] = node.type;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop all the nodes in the project
|
||||||
|
ProjectModel.instance.forEachComponent(handleComponent);
|
||||||
|
|
||||||
|
// Combine the result to look a little better.
|
||||||
|
const results: NodeReference[] = Array.from(references.keys())
|
||||||
|
.map((key) => ({
|
||||||
|
type: types[key],
|
||||||
|
displayName: types[key]?.displayName || key,
|
||||||
|
referenaces: references.get(key)
|
||||||
|
}))
|
||||||
|
.sort((a, b) => b.referenaces.length - a.referenaces.length);
|
||||||
|
|
||||||
|
HACK_nodeReferences = results;
|
||||||
|
setNodeReferences(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateIndex();
|
||||||
|
|
||||||
|
EventDispatcher.instance.on(
|
||||||
|
[
|
||||||
|
'Model.nodeAdded',
|
||||||
|
'Model.nodeRemoved',
|
||||||
|
'Model.componentAdded',
|
||||||
|
'Model.componentRemoved',
|
||||||
|
'Model.componentRenamed'
|
||||||
|
],
|
||||||
|
updateIndex,
|
||||||
|
group
|
||||||
|
);
|
||||||
|
|
||||||
|
return function () {
|
||||||
|
EventDispatcher.instance.off(group);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NodeReferencesContext.Provider
|
||||||
|
value={{
|
||||||
|
nodeReferences,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</NodeReferencesContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useNodeReferencesContext() {
|
||||||
|
const context = useContext(NodeReferencesContext);
|
||||||
|
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error('useNodeReferencesContext must be a child of NodeReferencesContextProvider');
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './NodeReferencesContext';
|
||||||
@@ -57,10 +57,13 @@ export class CloudFunctionAdapter extends NodeTypeAdapter {
|
|||||||
|
|
||||||
// Collect all cloud function components
|
// Collect all cloud function components
|
||||||
const functionRequestNodes = ProjectModel.instance.getNodesWithType('noodl.cloud.request');
|
const functionRequestNodes = ProjectModel.instance.getNodesWithType('noodl.cloud.request');
|
||||||
const functions = functionRequestNodes.map((r) => {
|
const functions = functionRequestNodes
|
||||||
const component = r.owner.owner;
|
.map((r) => {
|
||||||
return component.fullName;
|
const component = r.owner.owner;
|
||||||
});
|
return component.fullName;
|
||||||
|
})
|
||||||
|
// Remove all the Cloud Trigger functions
|
||||||
|
.filter((x) => !x.startsWith('/#__cloud__/__noodl_cloud_triggers__'));
|
||||||
|
|
||||||
ports.push({
|
ports.push({
|
||||||
plug: 'input',
|
plug: 'input',
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { NodeGraphContextProvider } from '@noodl-contexts/NodeGraphContext/NodeGraphContext';
|
import { NodeGraphContextProvider } from '@noodl-contexts/NodeGraphContext/NodeGraphContext';
|
||||||
|
import { NodeReferencesContextProvider } from '@noodl-contexts/NodeReferencesContext';
|
||||||
|
import { PluginContextProvider } from '@noodl-contexts/PluginContext';
|
||||||
import { ProjectDesignTokenContextProvider } from '@noodl-contexts/ProjectDesignTokenContext';
|
import { ProjectDesignTokenContextProvider } from '@noodl-contexts/ProjectDesignTokenContext';
|
||||||
import { useKeyboardCommands } from '@noodl-hooks/useKeyboardCommands';
|
import { useKeyboardCommands } from '@noodl-hooks/useKeyboardCommands';
|
||||||
import { useModel } from '@noodl-hooks/useModel';
|
import { useModel } from '@noodl-hooks/useModel';
|
||||||
@@ -43,7 +45,6 @@ import { BaseWindow } from '../../views/windows/BaseWindow';
|
|||||||
import { whatsnewRender } from '../../whats-new';
|
import { whatsnewRender } from '../../whats-new';
|
||||||
import { IRouteProps } from '../AppRoute';
|
import { IRouteProps } from '../AppRoute';
|
||||||
import { useSetupSettings } from './useSetupSettings';
|
import { useSetupSettings } from './useSetupSettings';
|
||||||
import { PluginContextProvider } from '@noodl-contexts/PluginContext';
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const ImportOverwritePopupTemplate = require('../../templates/importoverwritepopup.html');
|
const ImportOverwritePopupTemplate = require('../../templates/importoverwritepopup.html');
|
||||||
@@ -223,28 +224,30 @@ export function EditorPage({ route }: EditorPageProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<NodeGraphContextProvider>
|
<NodeGraphContextProvider>
|
||||||
<ProjectDesignTokenContextProvider>
|
<NodeReferencesContextProvider>
|
||||||
<PluginContextProvider>
|
<ProjectDesignTokenContextProvider>
|
||||||
<BaseWindow>
|
<PluginContextProvider>
|
||||||
{isLoading ? (
|
<BaseWindow>
|
||||||
<ActivityIndicator />
|
{isLoading ? (
|
||||||
) : (
|
<ActivityIndicator />
|
||||||
<>
|
) : (
|
||||||
<FrameDivider
|
<>
|
||||||
first={<SidePanel />}
|
<FrameDivider
|
||||||
second={<ErrorBoundary>{Boolean(Document) && <Document />}</ErrorBoundary>}
|
first={<SidePanel />}
|
||||||
sizeMin={200}
|
second={<ErrorBoundary>{Boolean(Document) && <Document />}</ErrorBoundary>}
|
||||||
size={frameDividerSize}
|
sizeMin={200}
|
||||||
horizontal
|
size={frameDividerSize}
|
||||||
onSizeChanged={setFrameDividerSize}
|
horizontal
|
||||||
/>
|
onSizeChanged={setFrameDividerSize}
|
||||||
|
/>
|
||||||
|
|
||||||
{Boolean(lesson) && <Frame instance={lesson} isContentSize isFitWidth />}
|
{Boolean(lesson) && <Frame instance={lesson} isContentSize isFitWidth />}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</BaseWindow>
|
</BaseWindow>
|
||||||
</PluginContextProvider>
|
</PluginContextProvider>
|
||||||
</ProjectDesignTokenContextProvider>
|
</ProjectDesignTokenContextProvider>
|
||||||
|
</NodeReferencesContextProvider>
|
||||||
</NodeGraphContextProvider>
|
</NodeGraphContextProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,14 @@ async function _writeFileToFolder({
|
|||||||
runtimeType
|
runtimeType
|
||||||
}: WriteFileToFolderArgs) {
|
}: WriteFileToFolderArgs) {
|
||||||
const fullPath = filesystem.join(getExternalFolderPath(), runtimeType, url);
|
const fullPath = filesystem.join(getExternalFolderPath(), runtimeType, url);
|
||||||
|
|
||||||
|
if (!filesystem.exists(fullPath)) {
|
||||||
|
// TODO: Save this warning somewhere, usually, this is not an issue though.
|
||||||
|
// This occurred because building in dev mode does not create the source map
|
||||||
|
// files which it expects to copy over.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let content = await filesystem.readFile(fullPath);
|
let content = await filesystem.readFile(fullPath);
|
||||||
let filename = url;
|
let filename = url;
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import { NodeGraphContextTmp } from '@noodl-contexts/NodeGraphContext/NodeGraphContext';
|
import { NodeGraphContextTmp } from '@noodl-contexts/NodeGraphContext/NodeGraphContext';
|
||||||
|
import { type NodeReference, useNodeReferencesContext } from '@noodl-contexts/NodeReferencesContext';
|
||||||
import { useFocusRefOnPanelActive } from '@noodl-hooks/useFocusRefOnPanelActive';
|
import { useFocusRefOnPanelActive } from '@noodl-hooks/useFocusRefOnPanelActive';
|
||||||
import { useNodeLibraryLoaded } from '@noodl-hooks/useNodeLibraryLoaded';
|
import { useNodeLibraryLoaded } from '@noodl-hooks/useNodeLibraryLoaded';
|
||||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { INodeColorScheme } from '@noodl-types/nodeTypes';
|
import { INodeColorScheme } from '@noodl-types/nodeTypes';
|
||||||
import { ComponentModel } from '@noodl-models/componentmodel';
|
import { NodeLibrary } from '@noodl-models/nodelibrary';
|
||||||
import { NodeGraphNode } from '@noodl-models/nodegraphmodel';
|
|
||||||
import { NodeLibrary, NodeLibraryNodeType } from '@noodl-models/nodelibrary';
|
|
||||||
import { BasicNodeType } from '@noodl-models/nodelibrary/BasicNodeType';
|
import { BasicNodeType } from '@noodl-models/nodelibrary/BasicNodeType';
|
||||||
import { ProjectModel } from '@noodl-models/projectmodel';
|
|
||||||
|
|
||||||
import { EditorNode } from '@noodl-core-ui/components/common/EditorNode';
|
import { EditorNode } from '@noodl-core-ui/components/common/EditorNode';
|
||||||
import { IconName, IconSize } from '@noodl-core-ui/components/common/Icon';
|
import { IconName, IconSize } from '@noodl-core-ui/components/common/Icon';
|
||||||
@@ -26,113 +24,17 @@ import { Section, SectionVariant } from '@noodl-core-ui/components/sidebar/Secti
|
|||||||
import { Label } from '@noodl-core-ui/components/typography/Label';
|
import { Label } from '@noodl-core-ui/components/typography/Label';
|
||||||
|
|
||||||
import { NodeReferencesPanel_ID } from '.';
|
import { NodeReferencesPanel_ID } from '.';
|
||||||
import { EventDispatcher } from '../../../../../shared/utils/EventDispatcher';
|
|
||||||
|
|
||||||
type ResultItem = {
|
|
||||||
type: NodeLibraryNodeType;
|
|
||||||
displayName: string;
|
|
||||||
referenaces: {
|
|
||||||
displayName: string;
|
|
||||||
node?: NodeGraphNode;
|
|
||||||
component: ComponentModel;
|
|
||||||
}[];
|
|
||||||
};
|
|
||||||
|
|
||||||
function useNodeReferences() {
|
|
||||||
const [group] = useState({});
|
|
||||||
const [result, setResult] = useState<ResultItem[]>([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
function updateIndex() {
|
|
||||||
const types: { [key: string]: ResultItem['type'] } = {};
|
|
||||||
const references = new Map<string, ResultItem['referenaces']>();
|
|
||||||
|
|
||||||
function handleComponent(component: ComponentModel) {
|
|
||||||
component.forEachNode((node: NodeGraphNode) => {
|
|
||||||
const name = node.type.name;
|
|
||||||
|
|
||||||
// Add the reference
|
|
||||||
references.set(name, [
|
|
||||||
...(references.get(name) || []),
|
|
||||||
{
|
|
||||||
displayName: component.displayName || component.name,
|
|
||||||
node,
|
|
||||||
component
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Repeater
|
|
||||||
if (name === 'For Each' && node.parameters.template) {
|
|
||||||
const templateComponent = ProjectModel.instance.getComponentWithName(node.parameters.template);
|
|
||||||
|
|
||||||
if (templateComponent) {
|
|
||||||
references.set(templateComponent.fullName, [
|
|
||||||
...(references.get(templateComponent.fullName) || []),
|
|
||||||
{
|
|
||||||
displayName: component.displayName || component.name,
|
|
||||||
node,
|
|
||||||
component
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
handleComponent(templateComponent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add some metadata for this node if we dont have it yet.
|
|
||||||
if (!types[name]) {
|
|
||||||
types[name] = node.type;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop all the nodes in the project
|
|
||||||
ProjectModel.instance.forEachComponent(handleComponent);
|
|
||||||
|
|
||||||
// Combine the result to look a little better.
|
|
||||||
const results: ResultItem[] = Array.from(references.keys())
|
|
||||||
.map((key) => ({
|
|
||||||
type: types[key],
|
|
||||||
displayName: types[key]?.displayName || key,
|
|
||||||
referenaces: references.get(key)
|
|
||||||
}))
|
|
||||||
.sort((a, b) => b.referenaces.length - a.referenaces.length);
|
|
||||||
|
|
||||||
setResult(results);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateIndex();
|
|
||||||
|
|
||||||
EventDispatcher.instance.on(
|
|
||||||
[
|
|
||||||
'Model.nodeAdded',
|
|
||||||
'Model.nodeRemoved',
|
|
||||||
'Model.componentAdded',
|
|
||||||
'Model.componentRemoved',
|
|
||||||
'Model.componentRenamed'
|
|
||||||
],
|
|
||||||
updateIndex,
|
|
||||||
group
|
|
||||||
);
|
|
||||||
|
|
||||||
return function () {
|
|
||||||
EventDispatcher.instance.off(group);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return [result];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function NodeReferencesPanel() {
|
export function NodeReferencesPanel() {
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [includeCoreNodes, setIncludeCoreNodes] = useState(false);
|
const [includeCoreNodes, setIncludeCoreNodes] = useState(false);
|
||||||
const inputRef = useRef(null);
|
const inputRef = useRef(null);
|
||||||
const [result] = useNodeReferences();
|
const { nodeReferences } = useNodeReferencesContext();
|
||||||
const nodeLibraryLoaded = useNodeLibraryLoaded();
|
const nodeLibraryLoaded = useNodeLibraryLoaded();
|
||||||
|
|
||||||
useFocusRefOnPanelActive(inputRef, NodeReferencesPanel_ID);
|
useFocusRefOnPanelActive(inputRef, NodeReferencesPanel_ID);
|
||||||
|
|
||||||
function searchFilter(x: ResultItem) {
|
function searchFilter(x: NodeReference) {
|
||||||
if (x.displayName.toLowerCase().includes(searchTerm)) {
|
if (x.displayName.toLowerCase().includes(searchTerm)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -144,7 +46,7 @@ export function NodeReferencesPanel() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let filteredResult = result.filter(searchFilter);
|
let filteredResult = nodeReferences.filter(searchFilter);
|
||||||
if (!includeCoreNodes) {
|
if (!includeCoreNodes) {
|
||||||
filteredResult = filteredResult.filter((x) => x.displayName.startsWith('/'));
|
filteredResult = filteredResult.filter((x) => x.displayName.startsWith('/'));
|
||||||
}
|
}
|
||||||
@@ -185,7 +87,7 @@ export function NodeReferencesPanel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ItemProps {
|
interface ItemProps {
|
||||||
entry: ResultItem;
|
entry: NodeReference;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Item({ entry }: ItemProps) {
|
function Item({ entry }: ItemProps) {
|
||||||
@@ -245,8 +147,8 @@ function Item({ entry }: ItemProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ItemReferenceProps {
|
interface ItemReferenceProps {
|
||||||
entry: ResultItem;
|
entry: NodeReference;
|
||||||
referenace: ResultItem['referenaces'][0];
|
referenace: NodeReference['referenaces'][0];
|
||||||
colors: INodeColorScheme;
|
colors: INodeColorScheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,11 @@ import { EventDispatcher } from '../../../../../shared/utils/EventDispatcher';
|
|||||||
import View from '../../../../../shared/view';
|
import View from '../../../../../shared/view';
|
||||||
import { NodeGraphEditor } from '../../nodegrapheditor';
|
import { NodeGraphEditor } from '../../nodegrapheditor';
|
||||||
import * as NewPopupLayer from '../../PopupLayer/index';
|
import * as NewPopupLayer from '../../PopupLayer/index';
|
||||||
|
import { type PopupMenuItem } from '../../PopupLayer/index';
|
||||||
import { ToastLayer } from '../../ToastLayer/ToastLayer';
|
import { ToastLayer } from '../../ToastLayer/ToastLayer';
|
||||||
import { ComponentsPanelFolder } from './ComponentsPanelFolder';
|
import { ComponentsPanelFolder } from './ComponentsPanelFolder';
|
||||||
import { ComponentTemplates } from './ComponentTemplates';
|
import { ComponentTemplates } from './ComponentTemplates';
|
||||||
|
import { HACK_findNodeReference } from '@noodl-contexts/NodeReferencesContext';
|
||||||
|
|
||||||
const PopupLayer = require('@noodl-views/popuplayer');
|
const PopupLayer = require('@noodl-views/popuplayer');
|
||||||
const ComponentsPanelTemplate = require('../../../templates/componentspanel.html');
|
const ComponentsPanelTemplate = require('../../../templates/componentspanel.html');
|
||||||
@@ -961,7 +963,7 @@ export class ComponentsPanelView extends View {
|
|||||||
forRuntimeType: this.getRuntimeType()
|
forRuntimeType: this.getRuntimeType()
|
||||||
});
|
});
|
||||||
|
|
||||||
let items: TSFixme[] = templates.map((t) => ({
|
let items: PopupMenuItem[] = templates.map((t) => ({
|
||||||
icon: IconName.Plus,
|
icon: IconName.Plus,
|
||||||
label: t.label,
|
label: t.label,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
@@ -987,6 +989,10 @@ export class ComponentsPanelView extends View {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find references
|
||||||
|
const nodeReference = HACK_findNodeReference(scope.comp.name);
|
||||||
|
const nodeReferencesText = `Used in ${nodeReference?.referenaces?.length || 0} places`;
|
||||||
|
|
||||||
items = items.concat([
|
items = items.concat([
|
||||||
{
|
{
|
||||||
icon: IconName.Pencil,
|
icon: IconName.Pencil,
|
||||||
@@ -1011,6 +1017,9 @@ export class ComponentsPanelView extends View {
|
|||||||
_this.onDeleteClicked(scope, el);
|
_this.onDeleteClicked(scope, el);
|
||||||
evt.stopPropagation();
|
evt.stopPropagation();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: nodeReferencesText
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -1110,6 +1119,16 @@ export class ComponentsPanelView extends View {
|
|||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if (scope.canBecomeRoot) {
|
||||||
|
// Find references
|
||||||
|
const nodeReference = HACK_findNodeReference(scope.folder.component.name);
|
||||||
|
const nodeReferencesText = `Used in ${nodeReference?.referenaces?.length || 0} places`;
|
||||||
|
|
||||||
|
items = items.concat([{
|
||||||
|
label: nodeReferencesText
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
|
||||||
const menu = new NewPopupLayer.PopupMenu({
|
const menu = new NewPopupLayer.PopupMenu({
|
||||||
items: items
|
items: items
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -73,8 +73,9 @@ class CloudStore {
|
|||||||
xhr.open(options.method || 'GET', this.endpoint + path, true);
|
xhr.open(options.method || 'GET', this.endpoint + path, true);
|
||||||
|
|
||||||
xhr.setRequestHeader('X-Parse-Application-Id', this.appId);
|
xhr.setRequestHeader('X-Parse-Application-Id', this.appId);
|
||||||
if (typeof _noodl_cloudservices !== 'undefined')
|
if (typeof _noodl_cloudservices !== 'undefined') {
|
||||||
xhr.setRequestHeader('X-Parse-Master-Key', _noodl_cloudservices.masterKey);
|
xhr.setRequestHeader('X-Parse-Master-Key', _noodl_cloudservices.masterKey);
|
||||||
|
}
|
||||||
|
|
||||||
// Check for current users
|
// Check for current users
|
||||||
var _cu = localStorage['Parse/' + this.appId + '/currentUser'];
|
var _cu = localStorage['Parse/' + this.appId + '/currentUser'];
|
||||||
@@ -191,13 +192,13 @@ class CloudStore {
|
|||||||
// I don't know which version the API was changed, lets just say above 4 for now.
|
// I don't know which version the API was changed, lets just say above 4 for now.
|
||||||
if (this.dbVersionMajor && this.dbVersionMajor > 4) {
|
if (this.dbVersionMajor && this.dbVersionMajor > 4) {
|
||||||
grouping._id = null;
|
grouping._id = null;
|
||||||
|
|
||||||
if (options.where) args.push('$match=' + encodeURIComponent(JSON.stringify(options.where)));
|
if (options.where) args.push('$match=' + encodeURIComponent(JSON.stringify(options.where)));
|
||||||
|
|
||||||
args.push('$group=' + JSON.stringify(grouping));
|
args.push('$group=' + JSON.stringify(grouping));
|
||||||
} else {
|
} else {
|
||||||
grouping.objectId = null;
|
grouping.objectId = null;
|
||||||
|
|
||||||
if (options.where) args.push('match=' + encodeURIComponent(JSON.stringify(options.where)));
|
if (options.where) args.push('match=' + encodeURIComponent(JSON.stringify(options.where)));
|
||||||
|
|
||||||
args.push('group=' + JSON.stringify(grouping));
|
args.push('group=' + JSON.stringify(grouping));
|
||||||
@@ -257,11 +258,22 @@ class CloudStore {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {{
|
||||||
|
* objectId: string;
|
||||||
|
* collection: string;
|
||||||
|
* include?: string[] | string;
|
||||||
|
* success: (data: unknown) => void;
|
||||||
|
* error: (error: unknown) => void;
|
||||||
|
* }} options
|
||||||
|
*/
|
||||||
fetch(options) {
|
fetch(options) {
|
||||||
const args = [];
|
const args = [];
|
||||||
|
|
||||||
if (options.include)
|
if (options.include) {
|
||||||
args.push('include=' + (Array.isArray(options.include) ? options.include.join(',') : options.include));
|
args.push('include=' + (Array.isArray(options.include) ? options.include.join(',') : options.include));
|
||||||
|
}
|
||||||
|
|
||||||
this._makeRequest(
|
this._makeRequest(
|
||||||
'/classes/' + options.collection + '/' + options.objectId + (args.length > 0 ? '?' + args.join('&') : ''),
|
'/classes/' + options.collection + '/' + options.objectId + (args.length > 0 ? '?' + args.join('&') : ''),
|
||||||
@@ -433,6 +445,8 @@ class CloudStore {
|
|||||||
* file: {
|
* file: {
|
||||||
* name: string;
|
* name: string;
|
||||||
* }
|
* }
|
||||||
|
* success: (data: unknown) => void;
|
||||||
|
* error: (error: unknown) => void;
|
||||||
* }} options
|
* }} options
|
||||||
*/
|
*/
|
||||||
deleteFile(options) {
|
deleteFile(options) {
|
||||||
@@ -563,21 +577,32 @@ function _deserializeJSON(data, type, modelScope) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _fromJSON(item, collectionName, modelScope) {
|
function _fromJSON(item, collectionName, modelScope) {
|
||||||
const m = (modelScope || Model).get(item.objectId);
|
const modelStore = modelScope || Model;
|
||||||
m._class = collectionName;
|
|
||||||
|
|
||||||
if (collectionName !== undefined && CloudStore._collections[collectionName] !== undefined)
|
const model = modelStore.get(item.objectId);
|
||||||
var schema = CloudStore._collections[collectionName].schema;
|
model._class = collectionName;
|
||||||
|
|
||||||
for (var key in item) {
|
let schema = undefined;
|
||||||
if (key === 'objectId' || key === 'ACL') continue;
|
if (collectionName !== undefined && CloudStore._collections[collectionName] !== undefined) {
|
||||||
|
schema = CloudStore._collections[collectionName].schema;
|
||||||
var _type = schema && schema.properties && schema.properties[key] ? schema.properties[key].type : undefined;
|
|
||||||
|
|
||||||
m.set(key, _deserializeJSON(item[key], _type, modelScope));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return m;
|
for (const key in item) {
|
||||||
|
if (key === 'objectId' || key === 'ACL') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const _type = schema && schema.properties && schema.properties[key] ? schema.properties[key].type : undefined;
|
||||||
|
model.set(key, _deserializeJSON(item[key], _type, modelScope));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the ACL roles to a global object
|
||||||
|
if (item.objectId && item.ACL) {
|
||||||
|
const aclModel = modelStore.get('--ndl--acl');
|
||||||
|
aclModel.set(item.objectId, item.ACL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
CloudStore._fromJSON = _fromJSON;
|
CloudStore._fromJSON = _fromJSON;
|
||||||
|
|||||||
@@ -36,16 +36,15 @@ function convertVisualFilter(query, options) {
|
|||||||
const _res = {};
|
const _res = {};
|
||||||
var cond;
|
var cond;
|
||||||
var value = query.input !== undefined ? inputs[query.input] : query.value;
|
var value = query.input !== undefined ? inputs[query.input] : query.value;
|
||||||
|
|
||||||
if (query.operator === 'exist') {
|
if (query.operator === 'exist') {
|
||||||
_res[query.property] = { $exists: true };
|
_res[query.property] = { $exists: true };
|
||||||
return _res;
|
return _res;
|
||||||
|
} else if (query.operator === 'not exist') {
|
||||||
|
_res[query.property] = { $exists: false };
|
||||||
|
return _res;
|
||||||
}
|
}
|
||||||
else if (query.operator === 'not exist') {
|
|
||||||
_res[query.property] = { $exists: false };
|
|
||||||
return _res;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value === undefined) return;
|
if (value === undefined) return;
|
||||||
|
|
||||||
if (CloudStore._collections[options.collectionName])
|
if (CloudStore._collections[options.collectionName])
|
||||||
@@ -80,7 +79,6 @@ function convertVisualFilter(query, options) {
|
|||||||
cond = { $regex: value, $options: 'i' };
|
cond = { $regex: value, $options: 'i' };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_res[query.property] = cond;
|
_res[query.property] = cond;
|
||||||
|
|
||||||
return _res;
|
return _res;
|
||||||
@@ -163,10 +161,22 @@ function _value(v) {
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Record<string, unknown>} filter
|
||||||
|
* @param {{
|
||||||
|
* collectionName?: string;
|
||||||
|
* modelScope?: unknown;
|
||||||
|
* error: (error: string) => void;
|
||||||
|
* }} options
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
function convertFilterOp(filter, options) {
|
function convertFilterOp(filter, options) {
|
||||||
const keys = Object.keys(filter);
|
const keys = Object.keys(filter);
|
||||||
if (keys.length === 0) return {};
|
if (keys.length === 0) return {};
|
||||||
if (keys.length !== 1) return options.error('Filter must only have one key found ' + keys.join(','));
|
if (keys.length !== 1) {
|
||||||
|
return options.error('Filter must only have one key found ' + keys.join(','));
|
||||||
|
}
|
||||||
|
|
||||||
const res = {};
|
const res = {};
|
||||||
const key = keys[0];
|
const key = keys[0];
|
||||||
@@ -179,18 +189,27 @@ function convertFilterOp(filter, options) {
|
|||||||
} else if (filter['idContainedIn'] !== undefined) {
|
} else if (filter['idContainedIn'] !== undefined) {
|
||||||
res['objectId'] = { $in: filter['idContainedIn'] };
|
res['objectId'] = { $in: filter['idContainedIn'] };
|
||||||
} else if (filter['relatedTo'] !== undefined) {
|
} else if (filter['relatedTo'] !== undefined) {
|
||||||
var modelId = filter['relatedTo']['id'];
|
const modelId = filter['relatedTo']['id'];
|
||||||
if (modelId === undefined) return options.error('Must provide id in relatedTo filter');
|
if (modelId === undefined) {
|
||||||
|
return options.error('Must provide id in relatedTo filter');
|
||||||
|
}
|
||||||
|
|
||||||
var relationKey = filter['relatedTo']['key'];
|
const relationKey = filter['relatedTo']['key'];
|
||||||
if (relationKey === undefined) return options.error('Must provide key in relatedTo filter');
|
if (relationKey === undefined) {
|
||||||
|
return options.error('Must provide key in relatedTo filter');
|
||||||
|
}
|
||||||
|
|
||||||
|
const className = filter['relatedTo']['className'] || (options.modelScope || Model).get(modelId)?._class;
|
||||||
|
if (typeof className === 'undefined') {
|
||||||
|
// Either the pointer is loaded as an object or we allow passing in the className.
|
||||||
|
return options.error('Must preload the Pointer or include className');
|
||||||
|
}
|
||||||
|
|
||||||
var m = (options.modelScope || Model).get(modelId);
|
|
||||||
res['$relatedTo'] = {
|
res['$relatedTo'] = {
|
||||||
object: {
|
object: {
|
||||||
__type: 'Pointer',
|
__type: 'Pointer',
|
||||||
objectId: modelId,
|
objectId: modelId,
|
||||||
className: m._class
|
className
|
||||||
},
|
},
|
||||||
key: relationKey
|
key: relationKey
|
||||||
};
|
};
|
||||||
@@ -208,13 +227,14 @@ function convertFilterOp(filter, options) {
|
|||||||
else if (opAndValue['containedIn'] !== undefined) res[key] = { $in: opAndValue['containedIn'] };
|
else if (opAndValue['containedIn'] !== undefined) res[key] = { $in: opAndValue['containedIn'] };
|
||||||
else if (opAndValue['notContainedIn'] !== undefined) res[key] = { $nin: opAndValue['notContainedIn'] };
|
else if (opAndValue['notContainedIn'] !== undefined) res[key] = { $nin: opAndValue['notContainedIn'] };
|
||||||
else if (opAndValue['pointsTo'] !== undefined) {
|
else if (opAndValue['pointsTo'] !== undefined) {
|
||||||
var m = (options.modelScope || Model).get(opAndValue['pointsTo']);
|
let schema = null;
|
||||||
if (CloudStore._collections[options.collectionName])
|
if (CloudStore._collections[options.collectionName]) {
|
||||||
var schema = CloudStore._collections[options.collectionName].schema;
|
schema = CloudStore._collections[options.collectionName].schema;
|
||||||
|
}
|
||||||
|
|
||||||
var targetClass =
|
const targetClass =
|
||||||
schema && schema.properties && schema.properties[key] ? schema.properties[key].targetClass : undefined;
|
schema && schema.properties && schema.properties[key] ? schema.properties[key].targetClass : undefined;
|
||||||
var type = schema && schema.properties && schema.properties[key] ? schema.properties[key].type : undefined;
|
const type = schema && schema.properties && schema.properties[key] ? schema.properties[key].type : undefined;
|
||||||
|
|
||||||
if (type === 'Relation') {
|
if (type === 'Relation') {
|
||||||
res[key] = {
|
res[key] = {
|
||||||
@@ -223,13 +243,13 @@ function convertFilterOp(filter, options) {
|
|||||||
className: targetClass
|
className: targetClass
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
if (Array.isArray(opAndValue['pointsTo']))
|
if (Array.isArray(opAndValue['pointsTo'])) {
|
||||||
res[key] = {
|
res[key] = {
|
||||||
$in: opAndValue['pointsTo'].map((v) => {
|
$in: opAndValue['pointsTo'].map((v) => {
|
||||||
return { __type: 'Pointer', objectId: v, className: targetClass };
|
return { __type: 'Pointer', objectId: v, className: targetClass };
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
else
|
} else {
|
||||||
res[key] = {
|
res[key] = {
|
||||||
$eq: {
|
$eq: {
|
||||||
__type: 'Pointer',
|
__type: 'Pointer',
|
||||||
@@ -237,6 +257,7 @@ function convertFilterOp(filter, options) {
|
|||||||
className: targetClass
|
className: targetClass
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (opAndValue['matchesRegex'] !== undefined) {
|
} else if (opAndValue['matchesRegex'] !== undefined) {
|
||||||
res[key] = {
|
res[key] = {
|
||||||
@@ -257,43 +278,42 @@ function convertFilterOp(filter, options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Geo points
|
// Geo points
|
||||||
} else if (opAndValue['nearSphere'] !== undefined) {
|
} else if (opAndValue['nearSphere'] !== undefined) {
|
||||||
var _v = opAndValue['nearSphere'];
|
var _v = opAndValue['nearSphere'];
|
||||||
res[key] = {
|
res[key] = {
|
||||||
$nearSphere: {
|
$nearSphere: {
|
||||||
__type: "GeoPoint",
|
__type: 'GeoPoint',
|
||||||
latitude: _v.latitude,
|
latitude: _v.latitude,
|
||||||
longitude: _v.longitude,
|
longitude: _v.longitude
|
||||||
},
|
},
|
||||||
$maxDistanceInMiles:_v.$maxDistanceInMiles,
|
$maxDistanceInMiles: _v.$maxDistanceInMiles,
|
||||||
$maxDistanceInKilometers:_v.maxDistanceInKilometers,
|
$maxDistanceInKilometers: _v.maxDistanceInKilometers,
|
||||||
$maxDistanceInRadians:_v.maxDistanceInRadians
|
$maxDistanceInRadians: _v.maxDistanceInRadians
|
||||||
};
|
};
|
||||||
} else if (opAndValue['withinBox'] !== undefined) {
|
} else if (opAndValue['withinBox'] !== undefined) {
|
||||||
var _v = opAndValue['withinBox'];
|
var _v = opAndValue['withinBox'];
|
||||||
res[key] = {
|
res[key] = {
|
||||||
$within:{
|
$within: {
|
||||||
$box: _v.map(gp => ({
|
$box: _v.map((gp) => ({
|
||||||
__type:"GeoPoint",
|
__type: 'GeoPoint',
|
||||||
latitude:gp.latitude,
|
latitude: gp.latitude,
|
||||||
longitude:gp.longitude
|
longitude: gp.longitude
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else if (opAndValue['withinPolygon'] !== undefined) {
|
} else if (opAndValue['withinPolygon'] !== undefined) {
|
||||||
var _v = opAndValue['withinPolygon'];
|
var _v = opAndValue['withinPolygon'];
|
||||||
res[key] = {
|
res[key] = {
|
||||||
$geoWithin:{
|
$geoWithin: {
|
||||||
$polygon: _v.map(gp => ({
|
$polygon: _v.map((gp) => ({
|
||||||
__type:"GeoPoint",
|
__type: 'GeoPoint',
|
||||||
latitude:gp.latitude,
|
latitude: gp.latitude,
|
||||||
longitude:gp.longitude
|
longitude: gp.longitude
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
options.error('Unrecognized filter keys ' + keys.join(','));
|
options.error('Unrecognized filter keys ' + keys.join(','));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ function createRecordsAPI(modelScope) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
async query(className, query, options) {
|
async query(className, query, options) {
|
||||||
if (typeof className === "undefined") throw new Error("'className' is undefined");
|
if (typeof className === 'undefined') throw new Error("'className' is undefined");
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
cloudstore().query({
|
cloudstore().query({
|
||||||
collection: className,
|
collection: className,
|
||||||
@@ -27,9 +27,9 @@ function createRecordsAPI(modelScope) {
|
|||||||
include: options ? options.include : undefined,
|
include: options ? options.include : undefined,
|
||||||
select: options ? options.select : undefined,
|
select: options ? options.select : undefined,
|
||||||
count: options ? options.count : undefined,
|
count: options ? options.count : undefined,
|
||||||
success: (results,count) => {
|
success: (results, count) => {
|
||||||
const _results = results.map((r) => cloudstore()._fromJSON(r, className));
|
const _results = results.map((r) => cloudstore()._fromJSON(r, className));
|
||||||
if(count !== undefined) resolve({results:_results,count});
|
if (count !== undefined) resolve({ results: _results, count });
|
||||||
else resolve(_results);
|
else resolve(_results);
|
||||||
},
|
},
|
||||||
error: (err) => {
|
error: (err) => {
|
||||||
@@ -40,7 +40,7 @@ function createRecordsAPI(modelScope) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async count(className, query) {
|
async count(className, query) {
|
||||||
if (typeof className === "undefined") throw new Error("'className' is undefined");
|
if (typeof className === 'undefined') throw new Error("'className' is undefined");
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
cloudstore().count({
|
cloudstore().count({
|
||||||
collection: className,
|
collection: className,
|
||||||
@@ -62,7 +62,7 @@ function createRecordsAPI(modelScope) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async distinct(className, property, query) {
|
async distinct(className, property, query) {
|
||||||
if (typeof className === "undefined") throw new Error("'className' is undefined");
|
if (typeof className === 'undefined') throw new Error("'className' is undefined");
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
cloudstore().distinct({
|
cloudstore().distinct({
|
||||||
collection: className,
|
collection: className,
|
||||||
@@ -85,7 +85,7 @@ function createRecordsAPI(modelScope) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async aggregate(className, group, query) {
|
async aggregate(className, group, query) {
|
||||||
if (typeof className === "undefined") throw new Error("'className' is undefined");
|
if (typeof className === 'undefined') throw new Error("'className' is undefined");
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
cloudstore().aggregate({
|
cloudstore().aggregate({
|
||||||
collection: className,
|
collection: className,
|
||||||
@@ -107,20 +107,31 @@ function createRecordsAPI(modelScope) {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string | { getId(): string; }} objectOrId
|
||||||
|
* @param {{
|
||||||
|
* className: string;
|
||||||
|
* include?: string[] | string;
|
||||||
|
* }} options
|
||||||
|
* @returns {Promise<unknown>}
|
||||||
|
*/
|
||||||
async fetch(objectOrId, options) {
|
async fetch(objectOrId, options) {
|
||||||
if (typeof objectOrId === 'undefined') return Promise.reject(new Error("'objectOrId' is undefined."));
|
if (typeof objectOrId === 'undefined') return Promise.reject(new Error("'objectOrId' is undefined."));
|
||||||
if (typeof objectOrId !== 'string') objectOrId = objectOrId.getId();
|
if (typeof objectOrId !== 'string') objectOrId = objectOrId.getId();
|
||||||
const className = (options ? options.className : undefined) || (modelScope || Model).get(objectOrId)._class;
|
const className = (options ? options.className : undefined) || (modelScope || Model).get(objectOrId)._class;
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!className) return reject('No class name specified');
|
if (!className) {
|
||||||
|
return reject('No class name specified');
|
||||||
|
}
|
||||||
|
|
||||||
cloudstore().fetch({
|
cloudstore().fetch({
|
||||||
collection: className,
|
collection: className,
|
||||||
objectId: objectOrId,
|
objectId: objectOrId,
|
||||||
include: options ? options.include : undefined,
|
include: options ? options.include : undefined,
|
||||||
success: function (response) {
|
success: function (response) {
|
||||||
var record = cloudstore()._fromJSON(response, className);
|
const record = cloudstore()._fromJSON(response, className);
|
||||||
resolve(record);
|
resolve(record);
|
||||||
},
|
},
|
||||||
error: function (err) {
|
error: function (err) {
|
||||||
@@ -186,7 +197,7 @@ function createRecordsAPI(modelScope) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async create(className, properties, options) {
|
async create(className, properties, options) {
|
||||||
if (typeof className === "undefined") throw new Error("'className' is undefined");
|
if (typeof className === 'undefined') throw new Error("'className' is undefined");
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
cloudstore().create({
|
cloudstore().create({
|
||||||
collection: className,
|
collection: className,
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ var DbCollectionNode = {
|
|||||||
_this.scheduleAfterInputsHaveUpdated(function () {
|
_this.scheduleAfterInputsHaveUpdated(function () {
|
||||||
_this.flagOutputDirty('count');
|
_this.flagOutputDirty('count');
|
||||||
_this.flagOutputDirty('firstItemId');
|
_this.flagOutputDirty('firstItemId');
|
||||||
|
_this.flagOutputDirty('isEmpty');
|
||||||
collectionChangedScheduled = false;
|
collectionChangedScheduled = false;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -66,6 +67,7 @@ var DbCollectionNode = {
|
|||||||
|
|
||||||
_this.flagOutputDirty('count');
|
_this.flagOutputDirty('count');
|
||||||
_this.flagOutputDirty('firstItemId');
|
_this.flagOutputDirty('firstItemId');
|
||||||
|
_this.flagOutputDirty('isEmpty');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.type === 'create') {
|
if (args.type === 'create') {
|
||||||
@@ -91,6 +93,7 @@ var DbCollectionNode = {
|
|||||||
|
|
||||||
_this.flagOutputDirty('count');
|
_this.flagOutputDirty('count');
|
||||||
_this.flagOutputDirty('firstItemId');
|
_this.flagOutputDirty('firstItemId');
|
||||||
|
_this.flagOutputDirty('isEmpty');
|
||||||
} else if (matchesQuery && !_this._internal.collection.contains(m)) {
|
} else if (matchesQuery && !_this._internal.collection.contains(m)) {
|
||||||
// It's not part of the result collection but now matches they query, add it and resort
|
// It's not part of the result collection but now matches they query, add it and resort
|
||||||
_addModelAtCorrectIndex(m);
|
_addModelAtCorrectIndex(m);
|
||||||
@@ -106,6 +109,7 @@ var DbCollectionNode = {
|
|||||||
|
|
||||||
_this.flagOutputDirty('count');
|
_this.flagOutputDirty('count');
|
||||||
_this.flagOutputDirty('firstItemId');
|
_this.flagOutputDirty('firstItemId');
|
||||||
|
_this.flagOutputDirty('isEmpty');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -153,6 +157,17 @@ var DbCollectionNode = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
isEmpty: {
|
||||||
|
type: 'boolean',
|
||||||
|
displayName: 'Is Empty',
|
||||||
|
group: 'General',
|
||||||
|
getter: function () {
|
||||||
|
if (this._internal.collection) {
|
||||||
|
return this._internal.collection.size() === 0;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
count: {
|
count: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
displayName: 'Count',
|
displayName: 'Count',
|
||||||
@@ -189,6 +204,7 @@ var DbCollectionNode = {
|
|||||||
setCollection: function (collection) {
|
setCollection: function (collection) {
|
||||||
this.bindCollection(collection);
|
this.bindCollection(collection);
|
||||||
this.flagOutputDirty('firstItemId');
|
this.flagOutputDirty('firstItemId');
|
||||||
|
this.flagOutputDirty('isEmpty');
|
||||||
this.flagOutputDirty('items');
|
this.flagOutputDirty('items');
|
||||||
this.flagOutputDirty('count');
|
this.flagOutputDirty('count');
|
||||||
},
|
},
|
||||||
@@ -257,7 +273,7 @@ var DbCollectionNode = {
|
|||||||
limit: limit,
|
limit: limit,
|
||||||
skip: skip,
|
skip: skip,
|
||||||
count: count,
|
count: count,
|
||||||
success: (results,count) => {
|
success: (results, count) => {
|
||||||
if (results !== undefined) {
|
if (results !== undefined) {
|
||||||
_c.set(
|
_c.set(
|
||||||
results.map((i) => {
|
results.map((i) => {
|
||||||
@@ -267,10 +283,9 @@ var DbCollectionNode = {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if(count !== undefined) {
|
if (count !== undefined) {
|
||||||
this._internal.storageSettings.storageTotalCount = count;
|
this._internal.storageSettings.storageTotalCount = count;
|
||||||
if(this.hasOutput('storageTotalCount'))
|
if (this.hasOutput('storageTotalCount')) this.flagOutputDirty('storageTotalCount');
|
||||||
this.flagOutputDirty('storageTotalCount');
|
|
||||||
}
|
}
|
||||||
this.setCollection(_c);
|
this.setCollection(_c);
|
||||||
this.sendSignalOnOutput('fetched');
|
this.sendSignalOnOutput('fetched');
|
||||||
@@ -383,7 +398,7 @@ var DbCollectionNode = {
|
|||||||
if (!storageSettings['storageEnableLimit']) return;
|
if (!storageSettings['storageEnableLimit']) return;
|
||||||
else return storageSettings['storageSkip'] || 0;
|
else return storageSettings['storageSkip'] || 0;
|
||||||
},
|
},
|
||||||
getStorageFetchTotalCount: function() {
|
getStorageFetchTotalCount: function () {
|
||||||
const storageSettings = this._internal.storageSettings;
|
const storageSettings = this._internal.storageSettings;
|
||||||
|
|
||||||
return !!storageSettings['storageEnableCount'];
|
return !!storageSettings['storageEnableCount'];
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
const { Node, EdgeTriggeredInput } = require('../../../../noodl-runtime');
|
const { Node, EdgeTriggeredInput } = require('../../../../noodl-runtime');
|
||||||
|
|
||||||
var Model = require('../../../model');
|
const Model = require('../../../model');
|
||||||
const CloudStore = require('../../../api/cloudstore');
|
const CloudStore = require('../../../api/cloudstore');
|
||||||
|
|
||||||
var ModelNodeDefinition = {
|
const ModelNodeDefinition = {
|
||||||
name: 'DbModel2',
|
name: 'DbModel2',
|
||||||
docs: 'https://docs.noodl.net/nodes/data/cloud-data/record',
|
docs: 'https://docs.noodl.net/nodes/data/cloud-data/record',
|
||||||
displayNodeName: 'Record',
|
displayNodeName: 'Record',
|
||||||
@@ -21,11 +21,11 @@ var ModelNodeDefinition = {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
initialize: function () {
|
initialize: function () {
|
||||||
var internal = this._internal;
|
const internal = this._internal;
|
||||||
internal.inputValues = {};
|
internal.inputValues = {};
|
||||||
internal.relationModelIds = {};
|
internal.relationModelIds = {};
|
||||||
|
|
||||||
var _this = this;
|
const _this = this;
|
||||||
this._internal.onModelChangedCallback = function (args) {
|
this._internal.onModelChangedCallback = function (args) {
|
||||||
if (_this.isInputConnected('fetch')) return;
|
if (_this.isInputConnected('fetch')) return;
|
||||||
|
|
||||||
@@ -109,13 +109,18 @@ var ModelNodeDefinition = {
|
|||||||
displayName: 'Id',
|
displayName: 'Id',
|
||||||
group: 'General',
|
group: 'General',
|
||||||
set: function (value) {
|
set: function (value) {
|
||||||
if (value instanceof Model) value = value.getId();
|
if (value instanceof Model) {
|
||||||
// Can be passed as model as well
|
// Can be passed as model as well
|
||||||
else if (typeof value === 'object') value = Model.create(value).getId(); // If this is an js object, dereference it
|
value = value.getId();
|
||||||
|
} else if (typeof value === 'object') {
|
||||||
|
// If this is an js object, dereference it
|
||||||
|
value = Model.create(value).getId();
|
||||||
|
}
|
||||||
|
|
||||||
this._internal.modelId = value; // Wait to fetch data
|
this._internal.modelId = value; // Wait to fetch data
|
||||||
if (this.isInputConnected('fetch') === false) this.setModelID(value);
|
if (this.isInputConnected('fetch') === false) {
|
||||||
else {
|
this.setModelID(value);
|
||||||
|
} else {
|
||||||
this.flagOutputDirty('id');
|
this.flagOutputDirty('id');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,9 +143,10 @@ var ModelNodeDefinition = {
|
|||||||
this.setModel(model);
|
this.setModel(model);
|
||||||
},
|
},
|
||||||
setModel: function (model) {
|
setModel: function (model) {
|
||||||
if (this._internal.model)
|
if (this._internal.model) {
|
||||||
// Remove old listener if existing
|
// Remove old listener if existing
|
||||||
this._internal.model.off('change', this._internal.onModelChangedCallback);
|
this._internal.model.off('change', this._internal.onModelChangedCallback);
|
||||||
|
}
|
||||||
|
|
||||||
this._internal.model = model;
|
this._internal.model = model;
|
||||||
this.flagOutputDirty('id');
|
this.flagOutputDirty('id');
|
||||||
@@ -148,7 +154,9 @@ var ModelNodeDefinition = {
|
|||||||
|
|
||||||
// We have a new model, mark all outputs as dirty
|
// We have a new model, mark all outputs as dirty
|
||||||
for (var key in model.data) {
|
for (var key in model.data) {
|
||||||
if (this.hasOutput('prop-' + key)) this.flagOutputDirty('prop-' + key);
|
if (this.hasOutput('prop-' + key)) {
|
||||||
|
this.flagOutputDirty('prop-' + key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.sendSignalOnOutput('fetched');
|
this.sendSignalOnOutput('fetched');
|
||||||
},
|
},
|
||||||
@@ -184,7 +192,7 @@ var ModelNodeDefinition = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
scheduleFetch: function () {
|
scheduleFetch: function () {
|
||||||
var _this = this;
|
const _this = this;
|
||||||
const internal = this._internal;
|
const internal = this._internal;
|
||||||
|
|
||||||
this.scheduleOnce('Fetch', function () {
|
this.scheduleOnce('Fetch', function () {
|
||||||
@@ -199,12 +207,13 @@ var ModelNodeDefinition = {
|
|||||||
collection: internal.collectionId,
|
collection: internal.collectionId,
|
||||||
objectId: internal.modelId, // Get the objectId part of the model id
|
objectId: internal.modelId, // Get the objectId part of the model id
|
||||||
success: function (response) {
|
success: function (response) {
|
||||||
var model = cloudstore._fromJSON(response, internal.collectionId);
|
const model = cloudstore._fromJSON(response, internal.collectionId);
|
||||||
if (internal.model !== model) {
|
if (internal.model !== model) {
|
||||||
// Check if we need to change model
|
// Check if we need to change model
|
||||||
if (internal.model)
|
if (internal.model) {
|
||||||
// Remove old listener if existing
|
// Remove old listener if existing
|
||||||
internal.model.off('change', internal.onModelChangedCallback);
|
internal.model.off('change', internal.onModelChangedCallback);
|
||||||
|
}
|
||||||
|
|
||||||
internal.model = model;
|
internal.model = model;
|
||||||
model.on('change', internal.onModelChangedCallback);
|
model.on('change', internal.onModelChangedCallback);
|
||||||
@@ -213,8 +222,10 @@ var ModelNodeDefinition = {
|
|||||||
|
|
||||||
delete response.objectId;
|
delete response.objectId;
|
||||||
|
|
||||||
for (var key in response) {
|
for (const key in response) {
|
||||||
if (_this.hasOutput('prop-' + key)) _this.flagOutputDirty('prop-' + key);
|
if (_this.hasOutput('prop-' + key)) {
|
||||||
|
_this.flagOutputDirty('prop-' + key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_this.sendSignalOnOutput('fetched');
|
_this.sendSignalOnOutput('fetched');
|
||||||
@@ -226,7 +237,6 @@ var ModelNodeDefinition = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
scheduleStore: function () {
|
scheduleStore: function () {
|
||||||
var _this = this;
|
|
||||||
var internal = this._internal;
|
var internal = this._internal;
|
||||||
if (!internal.model) return;
|
if (!internal.model) return;
|
||||||
|
|
||||||
@@ -247,8 +257,6 @@ var ModelNodeDefinition = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
registerInputIfNeeded: function (name) {
|
registerInputIfNeeded: function (name) {
|
||||||
var _this = this;
|
|
||||||
|
|
||||||
if (this.hasInput(name)) {
|
if (this.hasInput(name)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -328,8 +336,7 @@ function updatePorts(nodeId, parameters, editorConnection, graphModel) {
|
|||||||
var p = props[key];
|
var p = props[key];
|
||||||
if (ports.find((_p) => _p.name === key)) continue;
|
if (ports.find((_p) => _p.name === key)) continue;
|
||||||
|
|
||||||
if (p.type === 'Relation') {
|
if (p.type !== 'Relation') {
|
||||||
} else {
|
|
||||||
// Other schema type ports
|
// Other schema type ports
|
||||||
const _typeMap = {
|
const _typeMap = {
|
||||||
String: 'string',
|
String: 'string',
|
||||||
@@ -373,16 +380,16 @@ module.exports = {
|
|||||||
function _managePortsForNode(node) {
|
function _managePortsForNode(node) {
|
||||||
updatePorts(node.id, node.parameters, context.editorConnection, graphModel);
|
updatePorts(node.id, node.parameters, context.editorConnection, graphModel);
|
||||||
|
|
||||||
node.on('parameterUpdated', function (event) {
|
node.on('parameterUpdated', function () {
|
||||||
updatePorts(node.id, node.parameters, context.editorConnection, graphModel);
|
updatePorts(node.id, node.parameters, context.editorConnection, graphModel);
|
||||||
});
|
});
|
||||||
|
|
||||||
graphModel.on('metadataChanged.dbCollections', function (data) {
|
graphModel.on('metadataChanged.dbCollections', function () {
|
||||||
CloudStore.invalidateCollections();
|
CloudStore.invalidateCollections();
|
||||||
updatePorts(node.id, node.parameters, context.editorConnection, graphModel);
|
updatePorts(node.id, node.parameters, context.editorConnection, graphModel);
|
||||||
});
|
});
|
||||||
|
|
||||||
graphModel.on('metadataChanged.systemCollections', function (data) {
|
graphModel.on('metadataChanged.systemCollections', function () {
|
||||||
CloudStore.invalidateCollections();
|
CloudStore.invalidateCollections();
|
||||||
updatePorts(node.id, node.parameters, context.editorConnection, graphModel);
|
updatePorts(node.id, node.parameters, context.editorConnection, graphModel);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ const DateToStringNode = {
|
|||||||
|
|
||||||
this._internal.currentInput = _value;
|
this._internal.currentInput = _value;
|
||||||
this._format();
|
this._format();
|
||||||
this.flagOutputDirty('currentValue');
|
|
||||||
this.sendSignalOnOutput('inputChanged');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -49,30 +47,45 @@ const DateToStringNode = {
|
|||||||
type: 'signal',
|
type: 'signal',
|
||||||
displayName: 'Date Changed',
|
displayName: 'Date Changed',
|
||||||
group: 'Signals'
|
group: 'Signals'
|
||||||
|
},
|
||||||
|
onError: {
|
||||||
|
type: 'signal',
|
||||||
|
displayName: 'Invalid Date',
|
||||||
|
group: 'Signals'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
_format() {
|
_format() {
|
||||||
const t = this._internal.currentInput;
|
try {
|
||||||
const format = this._internal.formatString;
|
const t = this._internal.currentInput;
|
||||||
const date = ('0' + t.getDate()).slice(-2);
|
const format = this._internal.formatString;
|
||||||
const month = ('0' + (t.getMonth() + 1)).slice(-2);
|
const date = ('0' + t.getDate()).slice(-2);
|
||||||
const monthShort = new Intl.DateTimeFormat('en-US', { month: 'short' }).format(t);
|
const month = ('0' + (t.getMonth() + 1)).slice(-2);
|
||||||
const year = t.getFullYear();
|
const monthShort = new Intl.DateTimeFormat('en-US', { month: 'short' }).format(t);
|
||||||
const yearShort = year.toString().substring(2);
|
const year = t.getFullYear();
|
||||||
const hours = ('0' + t.getHours()).slice(-2);
|
const yearShort = year.toString().substring(2);
|
||||||
const minutes = ('0' + t.getMinutes()).slice(-2);
|
const hours = ('0' + t.getHours()).slice(-2);
|
||||||
const seconds = ('0' + t.getSeconds()).slice(-2);
|
const minutes = ('0' + t.getMinutes()).slice(-2);
|
||||||
|
const seconds = ('0' + t.getSeconds()).slice(-2);
|
||||||
|
|
||||||
this._internal.dateString = format
|
this._internal.dateString = format
|
||||||
.replace(/\{date\}/g, date)
|
.replace(/\{date\}/g, date)
|
||||||
.replace(/\{month\}/g, month)
|
.replace(/\{month\}/g, month)
|
||||||
.replace(/\{monthShort\}/g, monthShort)
|
.replace(/\{monthShort\}/g, monthShort)
|
||||||
.replace(/\{year\}/g, year)
|
.replace(/\{year\}/g, year)
|
||||||
.replace(/\{yearShort\}/g, yearShort)
|
.replace(/\{yearShort\}/g, yearShort)
|
||||||
.replace(/\{hours\}/g, hours)
|
.replace(/\{hours\}/g, hours)
|
||||||
.replace(/\{minutes\}/g, minutes)
|
.replace(/\{minutes\}/g, minutes)
|
||||||
.replace(/\{seconds\}/g, seconds);
|
.replace(/\{seconds\}/g, seconds);
|
||||||
|
} catch (error) {
|
||||||
|
// Set the output to be blank, makes it easier to handle.
|
||||||
|
this._internal.dateString = '';
|
||||||
|
this.flagOutputDirty('onError');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flag that the value have changed
|
||||||
|
this.flagOutputDirty('currentValue');
|
||||||
|
this.sendSignalOnOutput('inputChanged');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
// Allows to define the output path of the files built by the viewer.
|
// Allows to define the output path of the files built by the viewer.
|
||||||
//
|
//
|
||||||
// For example in the CLI, we will also build this, just with a different output path.
|
// For example in the CLI, we will also build this, just with a different output path.
|
||||||
outPath: process.env.OUT_PATH || path.resolve(__dirname, '../../noodl-editor/src/external'),
|
outPath: process.env.OUT_PATH || path.resolve(__dirname, '../../noodl-editor/src/external'),
|
||||||
runtimeVersion: 'cloud-runtime-' + require('../package.json').version.replaceAll('.', '-')
|
runtimeVersion: 'cloud-runtime-' + require('../package.json').version.replaceAll('.', '-')
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
const { merge } = require("webpack-merge");
|
const { merge } = require('webpack-merge');
|
||||||
const common = require("./webpack.viewer.common.js");
|
const common = require('./webpack.viewer.common.js');
|
||||||
|
|
||||||
module.exports = merge(common, {
|
module.exports = merge(common, {
|
||||||
mode: "development",
|
mode: 'development',
|
||||||
devtool: "inline-source-map",
|
devtool: 'inline-source-map',
|
||||||
watch: true,
|
watch: true
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,5 +2,6 @@ const { merge } = require('webpack-merge');
|
|||||||
const common = require('./webpack.viewer.common.js');
|
const common = require('./webpack.viewer.common.js');
|
||||||
|
|
||||||
module.exports = merge(common, {
|
module.exports = merge(common, {
|
||||||
mode: 'production'
|
mode: 'production',
|
||||||
|
devtool: 'source-map'
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const { Node } = require('@noodl/runtime');
|
const { Node } = require('@noodl/runtime');
|
||||||
|
const Model = require('@noodl/runtime/src/model');
|
||||||
|
|
||||||
var Model = require('@noodl/runtime/src/model');
|
const VariableNodeDefinition = {
|
||||||
|
|
||||||
var VariableNodeDefinition = {
|
|
||||||
name: 'Variable',
|
name: 'Variable',
|
||||||
docs: 'https://docs.noodl.net/nodes/data/variable',
|
docs: 'https://docs.noodl.net/nodes/data/variable',
|
||||||
category: 'Data',
|
category: 'Data',
|
||||||
|
|||||||
@@ -192,8 +192,7 @@ function setup(context, graphModel) {
|
|||||||
enums: pages.map((p) => ({
|
enums: pages.map((p) => ({
|
||||||
label: p.label,
|
label: p.label,
|
||||||
value: p.id
|
value: p.id
|
||||||
})),
|
}))
|
||||||
allowEditOnly: true
|
|
||||||
},
|
},
|
||||||
group: 'General',
|
group: 'General',
|
||||||
displayName: 'Target Page',
|
displayName: 'Target Page',
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
import ASyncQueue from '../../async-queue';
|
import ASyncQueue from '../../async-queue';
|
||||||
import { createNodeFromReactComponent } from '../../react-component-node';
|
import { createNodeFromReactComponent } from '../../react-component-node';
|
||||||
|
|
||||||
@@ -75,10 +77,13 @@ const PageStack = {
|
|||||||
const info = [{ type: 'text', value: 'Active Components:' }];
|
const info = [{ type: 'text', value: 'Active Components:' }];
|
||||||
|
|
||||||
return info.concat(
|
return info.concat(
|
||||||
this._internal.stack.map((p, i) => ({
|
this._internal.stack.map((p) => {
|
||||||
type: 'text',
|
const pageInfo = this._findPage(p.pageId);
|
||||||
value: '- ' + this._internal.pages.find((pi) => pi.id === p.pageId).label
|
return {
|
||||||
}))
|
type: 'text',
|
||||||
|
value: '- ' + pageInfo.label
|
||||||
|
};
|
||||||
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
defaultCss: {
|
defaultCss: {
|
||||||
@@ -170,6 +175,7 @@ const PageStack = {
|
|||||||
topPageName: {
|
topPageName: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
displayName: 'Top Component Name',
|
displayName: 'Top Component Name',
|
||||||
|
group: 'General',
|
||||||
get() {
|
get() {
|
||||||
return this._internal.topPageName;
|
return this._internal.topPageName;
|
||||||
}
|
}
|
||||||
@@ -177,6 +183,7 @@ const PageStack = {
|
|||||||
stackDepth: {
|
stackDepth: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
displayName: 'Stack Depth',
|
displayName: 'Stack Depth',
|
||||||
|
group: 'General',
|
||||||
get() {
|
get() {
|
||||||
return this._internal.stackDepth;
|
return this._internal.stackDepth;
|
||||||
}
|
}
|
||||||
@@ -189,12 +196,31 @@ const PageStack = {
|
|||||||
_deregisterPageStack() {
|
_deregisterPageStack() {
|
||||||
NavigationHandler.instance.deregisterPageStack(this._internal.name, this);
|
NavigationHandler.instance.deregisterPageStack(this._internal.name, this);
|
||||||
},
|
},
|
||||||
_pageNameForId(id) {
|
/**
|
||||||
if (this._internal.pages === undefined) return;
|
* @param {String} pageIdOrLabel
|
||||||
const page = this._internal.pages.find((p) => p.id === id);
|
*/
|
||||||
if (page === undefined) return;
|
_findPage(pageIdOrLabel) {
|
||||||
|
if (this._internal.pageInfo[pageIdOrLabel]) {
|
||||||
|
const pageInfo = this._internal.pageInfo[pageIdOrLabel];
|
||||||
|
const pageRef = this._internal.pages.find((x) => x.id === pageIdOrLabel);
|
||||||
|
return {
|
||||||
|
component: String(pageInfo.component),
|
||||||
|
label: String(pageRef.label),
|
||||||
|
id: String(pageIdOrLabel)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return page.label;
|
const pageRef = this._internal.pages.find((x) => x.label === pageIdOrLabel);
|
||||||
|
if (pageRef) {
|
||||||
|
const pageInfo = this._internal.pageInfo[pageRef.id];
|
||||||
|
return {
|
||||||
|
component: String(pageInfo.component),
|
||||||
|
label: String(pageRef.label),
|
||||||
|
id: String(pageRef.id)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
},
|
},
|
||||||
setPageOutputs(outputs) {
|
setPageOutputs(outputs) {
|
||||||
for (const prop in outputs) {
|
for (const prop in outputs) {
|
||||||
@@ -230,8 +256,9 @@ const PageStack = {
|
|||||||
|
|
||||||
if (this._internal.pages === undefined || this._internal.pages.length === 0) return;
|
if (this._internal.pages === undefined || this._internal.pages.length === 0) return;
|
||||||
|
|
||||||
var startPageId,
|
let startPageId;
|
||||||
params = {};
|
let params = {};
|
||||||
|
|
||||||
var pageFromUrl = this.matchPageFromUrl();
|
var pageFromUrl = this.matchPageFromUrl();
|
||||||
if (pageFromUrl !== undefined) {
|
if (pageFromUrl !== undefined) {
|
||||||
// We have an url matching a page, use that page as start page
|
// We have an url matching a page, use that page as start page
|
||||||
@@ -239,13 +266,16 @@ const PageStack = {
|
|||||||
|
|
||||||
params = Object.assign({}, pageFromUrl.query, pageFromUrl.params);
|
params = Object.assign({}, pageFromUrl.query, pageFromUrl.params);
|
||||||
} else {
|
} else {
|
||||||
var startPageId = this._internal.startPageId;
|
startPageId = this._internal.startPageId;
|
||||||
if (startPageId === undefined) startPageId = this._internal.pages[0].id;
|
if (startPageId === undefined) startPageId = this._internal.pages[0].id;
|
||||||
}
|
}
|
||||||
|
|
||||||
var pageInfo = this._internal.pageInfo[startPageId];
|
// Find the page by either ID or by Label
|
||||||
|
const pageInfo = this._findPage(startPageId);
|
||||||
if (pageInfo === undefined || pageInfo.component === undefined) return; // No component specified for page
|
if (pageInfo === undefined || pageInfo.component === undefined) {
|
||||||
|
// No page was found
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var content = await this.nodeScope.createNode(pageInfo.component, guid());
|
var content = await this.nodeScope.createNode(pageInfo.component, guid());
|
||||||
|
|
||||||
@@ -269,7 +299,7 @@ const PageStack = {
|
|||||||
];
|
];
|
||||||
|
|
||||||
this.setPageOutputs({
|
this.setPageOutputs({
|
||||||
topPageName: this._pageNameForId(startPageId),
|
topPageName: pageInfo.label,
|
||||||
stackDepth: this._internal.stack.length
|
stackDepth: this._internal.stack.length
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -458,13 +488,22 @@ const PageStack = {
|
|||||||
this._internal.asyncQueue.enqueue(this.replaceAsync.bind(this, args));
|
this._internal.asyncQueue.enqueue(this.replaceAsync.bind(this, args));
|
||||||
},
|
},
|
||||||
async replaceAsync(args) {
|
async replaceAsync(args) {
|
||||||
if (this._internal.pages === undefined || this._internal.pages.length === 0) return;
|
if (this._internal.pages === undefined || this._internal.pages.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this._internal.isTransitioning) return;
|
if (this._internal.isTransitioning) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var pageId = args.target || this._internal.pages[0].id;
|
const pageId = args.target || this._internal.pages[0].id;
|
||||||
var pageInfo = this._internal.pageInfo[pageId];
|
|
||||||
if (pageInfo === undefined || pageInfo.component === undefined) return; // No component specified for page
|
// Find the page by either ID or by Label
|
||||||
|
const pageInfo = this._findPage(pageId);
|
||||||
|
if (pageInfo === undefined || pageInfo.component === undefined) {
|
||||||
|
// No page was found
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Remove all current pages in the stack
|
// Remove all current pages in the stack
|
||||||
var children = this.getChildren();
|
var children = this.getChildren();
|
||||||
@@ -498,7 +537,7 @@ const PageStack = {
|
|||||||
];
|
];
|
||||||
|
|
||||||
this.setPageOutputs({
|
this.setPageOutputs({
|
||||||
topPageName: this._pageNameForId(pageId),
|
topPageName: pageInfo.label,
|
||||||
stackDepth: this._internal.stack.length
|
stackDepth: this._internal.stack.length
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -510,13 +549,22 @@ const PageStack = {
|
|||||||
this._internal.asyncQueue.enqueue(this.navigateAsync.bind(this, args));
|
this._internal.asyncQueue.enqueue(this.navigateAsync.bind(this, args));
|
||||||
},
|
},
|
||||||
async navigateAsync(args) {
|
async navigateAsync(args) {
|
||||||
if (this._internal.pages === undefined || this._internal.pages.length === 0) return;
|
if (this._internal.pages === undefined || this._internal.pages.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this._internal.isTransitioning) return;
|
if (this._internal.isTransitioning) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var pageId = args.target || this._internal.pages[0].id;
|
const pageId = args.target || this._internal.pages[0].id;
|
||||||
var pageInfo = this._internal.pageInfo[pageId];
|
|
||||||
if (pageInfo === undefined || pageInfo.component === undefined) return; // No component specified for page
|
// Find the page by either ID or by Label
|
||||||
|
const pageInfo = this._findPage(pageId);
|
||||||
|
if (pageInfo === undefined || pageInfo.component === undefined) {
|
||||||
|
// No page was found
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Create the container group
|
// Create the container group
|
||||||
const group = this.createPageContainer();
|
const group = this.createPageContainer();
|
||||||
@@ -530,7 +578,7 @@ const PageStack = {
|
|||||||
group.addChild(content);
|
group.addChild(content);
|
||||||
|
|
||||||
// Connect navigate back nodes
|
// Connect navigate back nodes
|
||||||
var navigateBackNodes = content.nodeScope.getNodesWithType('PageStackNavigateBack');
|
const navigateBackNodes = content.nodeScope.getNodesWithType('PageStackNavigateBack');
|
||||||
if (navigateBackNodes && navigateBackNodes.length > 0) {
|
if (navigateBackNodes && navigateBackNodes.length > 0) {
|
||||||
for (var j = 0; j < navigateBackNodes.length; j++) {
|
for (var j = 0; j < navigateBackNodes.length; j++) {
|
||||||
navigateBackNodes[j]._setBackCallback(this.back.bind(this));
|
navigateBackNodes[j]._setBackCallback(this.back.bind(this));
|
||||||
@@ -538,8 +586,8 @@ const PageStack = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Push the new top
|
// Push the new top
|
||||||
var top = this._internal.stack[this._internal.stack.length - 1];
|
const top = this._internal.stack[this._internal.stack.length - 1];
|
||||||
var newTop = {
|
const newTop = {
|
||||||
from: top.page,
|
from: top.page,
|
||||||
page: group,
|
page: group,
|
||||||
pageInfo: pageInfo,
|
pageInfo: pageInfo,
|
||||||
@@ -551,7 +599,7 @@ const PageStack = {
|
|||||||
};
|
};
|
||||||
this._internal.stack.push(newTop);
|
this._internal.stack.push(newTop);
|
||||||
this.setPageOutputs({
|
this.setPageOutputs({
|
||||||
topPageName: this._pageNameForId(args.target),
|
topPageName: pageInfo.label,
|
||||||
stackDepth: this._internal.stack.length
|
stackDepth: this._internal.stack.length
|
||||||
});
|
});
|
||||||
this._updateUrlWithTopPage();
|
this._updateUrlWithTopPage();
|
||||||
@@ -584,8 +632,11 @@ const PageStack = {
|
|||||||
this.addChild(top.from, 0);
|
this.addChild(top.from, 0);
|
||||||
top.backCallback && top.backCallback(args.backAction, args.results);
|
top.backCallback && top.backCallback(args.backAction, args.results);
|
||||||
|
|
||||||
|
// Find the page by either ID or by Label
|
||||||
|
const pageInfo = this._findPage(this._internal.stack[this._internal.stack.length - 2].pageId);
|
||||||
|
|
||||||
this.setPageOutputs({
|
this.setPageOutputs({
|
||||||
topPageName: this._pageNameForId(this._internal.stack[this._internal.stack.length - 2].pageId),
|
topPageName: pageInfo.label,
|
||||||
stackDepth: this._internal.stack.length - 1
|
stackDepth: this._internal.stack.length - 1
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
const { Node } = require('@noodl/runtime');
|
const { Node } = require('@noodl/runtime');
|
||||||
|
|
||||||
var Model = require('@noodl/runtime/src/model'),
|
const Collection = require('@noodl/runtime/src/collection');
|
||||||
Collection = require('@noodl/runtime/src/collection');
|
|
||||||
|
|
||||||
var CollectionNode = {
|
var CollectionNode = {
|
||||||
name: 'Collection2',
|
name: 'Collection2',
|
||||||
@@ -27,6 +26,7 @@ var CollectionNode = {
|
|||||||
|
|
||||||
_this.scheduleAfterInputsHaveUpdated(function () {
|
_this.scheduleAfterInputsHaveUpdated(function () {
|
||||||
_this.sendSignalOnOutput('changed');
|
_this.sendSignalOnOutput('changed');
|
||||||
|
_this.flagOutputDirty('firstItemId');
|
||||||
_this.flagOutputDirty('count');
|
_this.flagOutputDirty('count');
|
||||||
collectionChangedScheduled = false;
|
collectionChangedScheduled = false;
|
||||||
});
|
});
|
||||||
@@ -117,6 +117,17 @@ var CollectionNode = {
|
|||||||
return this._internal.collection;
|
return this._internal.collection;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
firstItemId: {
|
||||||
|
type: 'string',
|
||||||
|
displayName: 'First Item Id',
|
||||||
|
group: 'General',
|
||||||
|
getter: function () {
|
||||||
|
if (this._internal.collection) {
|
||||||
|
var firstItem = this._internal.collection.get(0);
|
||||||
|
if (firstItem !== undefined) return firstItem.getId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
count: {
|
count: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
displayName: 'Count',
|
displayName: 'Count',
|
||||||
@@ -150,6 +161,7 @@ var CollectionNode = {
|
|||||||
collection.on('change', this._internal.collectionChangedCallback);
|
collection.on('change', this._internal.collectionChangedCallback);
|
||||||
|
|
||||||
this.flagOutputDirty('items');
|
this.flagOutputDirty('items');
|
||||||
|
this.flagOutputDirty('firstItemId');
|
||||||
this.flagOutputDirty('count');
|
this.flagOutputDirty('count');
|
||||||
},
|
},
|
||||||
setSourceCollection: function (collection) {
|
setSourceCollection: function (collection) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
{"url":"noodl-app.png"},
|
{"url":"noodl-app.png"},
|
||||||
{"url":"load_terminator.js"},
|
{"url":"load_terminator.js"},
|
||||||
{"url":"noodl.deploy.js"},
|
{"url":"noodl.deploy.js"},
|
||||||
|
{"url":"noodl.deploy.js.map"},
|
||||||
{"url":"react.production.min.js"},
|
{"url":"react.production.min.js"},
|
||||||
{"url":"react-dom.production.min.js"}
|
{"url":"react-dom.production.min.js"}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -482,7 +482,12 @@ declare namespace Noodl {
|
|||||||
const Records: RecordsApi;
|
const Records: RecordsApi;
|
||||||
|
|
||||||
interface CurrentUserObject {
|
interface CurrentUserObject {
|
||||||
UserId: string;
|
id: string;
|
||||||
|
email: string;
|
||||||
|
emailVerified: boolean;
|
||||||
|
username: string;
|
||||||
|
|
||||||
|
Properties: unknown;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log out the current user and terminate the session.
|
* Log out the current user and terminate the session.
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
const path = require("path");
|
const path = require('path');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
// Allows to define the output path of the files built by the viewer.
|
// Allows to define the output path of the files built by the viewer.
|
||||||
//
|
//
|
||||||
// For example in the CLI, we will also build this, just with a different output path.
|
// For example in the CLI, we will also build this, just with a different output path.
|
||||||
outPath:
|
outPath: process.env.OUT_PATH || path.resolve(__dirname, '../../noodl-editor/src/external')
|
||||||
process.env.OUT_PATH ||
|
};
|
||||||
path.resolve(__dirname, "../../noodl-editor/src/external"),
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ module.exports = {
|
|||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.tsx', '.ts', '.jsx', '.js'],
|
extensions: ['.tsx', '.ts', '.jsx', '.js'],
|
||||||
fallback: {
|
fallback: {
|
||||||
events: require.resolve('events/'),
|
events: require.resolve('events/')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
const { merge } = require("webpack-merge");
|
const { merge } = require('webpack-merge');
|
||||||
const common = require("./webpack.deploy.common.js");
|
const common = require('./webpack.deploy.common.js');
|
||||||
|
|
||||||
module.exports = merge(common, {
|
module.exports = merge(common, {
|
||||||
mode: "development",
|
mode: 'development',
|
||||||
devtool: "inline-source-map",
|
devtool: 'inline-source-map',
|
||||||
watch: true,
|
watch: true
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
const { merge } = require("webpack-merge");
|
const { merge } = require('webpack-merge');
|
||||||
const common = require("./webpack.deploy.common.js");
|
const common = require('./webpack.deploy.common.js');
|
||||||
|
|
||||||
module.exports = merge(common, {
|
module.exports = merge(common, {
|
||||||
mode: "production",
|
mode: 'production',
|
||||||
|
devtool: 'source-map'
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ module.exports = {
|
|||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.tsx', '.ts', '.jsx', '.js'],
|
extensions: ['.tsx', '.ts', '.jsx', '.js'],
|
||||||
fallback: {
|
fallback: {
|
||||||
events: require.resolve('events/'),
|
events: require.resolve('events/')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
|
|||||||
@@ -2,5 +2,6 @@ const { merge } = require('webpack-merge');
|
|||||||
const common = require('./webpack.ssr.common.js');
|
const common = require('./webpack.ssr.common.js');
|
||||||
|
|
||||||
module.exports = merge(common, {
|
module.exports = merge(common, {
|
||||||
mode: 'production'
|
mode: 'production',
|
||||||
|
devtool: 'source-map'
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
const { merge } = require("webpack-merge");
|
const { merge } = require('webpack-merge');
|
||||||
const common = require("./webpack.viewer.common.js");
|
const common = require('./webpack.viewer.common.js');
|
||||||
|
|
||||||
module.exports = merge(common, {
|
module.exports = merge(common, {
|
||||||
mode: "development",
|
mode: 'development',
|
||||||
devtool: "inline-source-map",
|
devtool: 'inline-source-map',
|
||||||
watch: true,
|
watch: true
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
const { merge } = require("webpack-merge");
|
const { merge } = require('webpack-merge');
|
||||||
const common = require("./webpack.viewer.common.js");
|
const common = require('./webpack.viewer.common.js');
|
||||||
|
|
||||||
module.exports = merge(common, {
|
module.exports = merge(common, {
|
||||||
mode: "production",
|
mode: 'production',
|
||||||
|
devtool: 'source-map'
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user