First commit
This commit is contained in:
249
Animations.md
Normal file
249
Animations.md
Normal file
@@ -0,0 +1,249 @@
|
||||
|
||||
# Animations in Unify
|
||||
|
||||
Unify's animation system provides a **declarative and flexible way** to apply smooth transitions, visual effects, and interactive animations to your UI components. Whether it's for simple property changes, complex transitions, or interactive user feedback, Unify makes it easy to animate your components in a clear and structured way.
|
||||
|
||||
---
|
||||
|
||||
## 1. Introduction to Animations
|
||||
|
||||
Animations in Unify are **object-driven**, meaning animations are attached directly to **Unify objects** such as panels, buttons, and other UI components. The animation system uses **keyframes** to define transitions over time, allowing properties like `opacity`, `transform`, `background`, etc., to change smoothly.
|
||||
|
||||
Animations are executed based on **events** (e.g., `click`, `load`, `hover`) or can be triggered explicitly using the `play()` method.
|
||||
|
||||
---
|
||||
|
||||
## 2. Basic Animation Example
|
||||
|
||||
To define an animation, you typically use the **`createAnimation`** method, which creates a new animation instance. You can then define **keyframes** and set the properties for the element at various points during the animation.
|
||||
|
||||
### Example: Color Change Animation
|
||||
|
||||
Here’s an example where we animate the background color of an element from **blue** to **green**:
|
||||
|
||||
```js
|
||||
class ColorChangeAnimation {
|
||||
|
||||
element = document.createElement("div");
|
||||
width = 100;
|
||||
height = 100;
|
||||
margin = 20;
|
||||
|
||||
create() {
|
||||
// Create the animation
|
||||
this.animation = this.createAnimation("colorChange");
|
||||
|
||||
// Define keyframes
|
||||
var key = this.animation.createKeyFrame(0);
|
||||
key.setProperty("background", "#03a9f4"); // Initial color
|
||||
|
||||
key = this.animation.createKeyFrame(100);
|
||||
key.setProperty("background", "#a6e22e"); // Final color
|
||||
}
|
||||
|
||||
async click() {
|
||||
// Play the animation over 2 seconds when clicked
|
||||
await this.animation.play("2s");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Key Concepts:
|
||||
|
||||
* **Create the animation**: The `createAnimation("colorChange")` method creates a new animation instance.
|
||||
* **Define keyframes**: Keyframes define the start and end states of the animation (e.g., `background` changing from blue to green).
|
||||
* **Trigger the animation**: The `play()` method starts the animation and allows you to define its duration (e.g., `2s` for 2 seconds).
|
||||
|
||||
---
|
||||
|
||||
## 3. Animating Multiple Properties
|
||||
|
||||
You can animate multiple properties simultaneously by setting **multiple keyframes** for different CSS properties. This allows you to create more complex animations that change several aspects of the UI at once.
|
||||
|
||||
### Example: Fading and Scaling
|
||||
|
||||
Here’s an example of an animation that fades an element in while simultaneously scaling it up:
|
||||
|
||||
```js
|
||||
class FadeAndScale {
|
||||
|
||||
element = document.createElement("div");
|
||||
width = 100;
|
||||
height = 100;
|
||||
margin = 20;
|
||||
|
||||
create() {
|
||||
// Create the animation
|
||||
this.animation = this.createAnimation("fadeAndScale");
|
||||
|
||||
// Fade in
|
||||
var key = this.animation.createKeyFrame(0);
|
||||
key.setProperty("opacity", "0");
|
||||
key.setProperty("transform", "scale(0.5)");
|
||||
|
||||
// Fade and scale up
|
||||
key = this.animation.createKeyFrame(100);
|
||||
key.setProperty("opacity", "1");
|
||||
key.setProperty("transform", "scale(1)");
|
||||
}
|
||||
|
||||
async click() {
|
||||
// Play the fade and scale animation
|
||||
await this.animation.play("2s");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example:
|
||||
|
||||
* **Opacity** and **transform** properties are animated together.
|
||||
* The element fades in from `opacity: 0` to `opacity: 1` while scaling from `scale(0.5)` to `scale(1)`.
|
||||
|
||||
---
|
||||
|
||||
## 4. Triggering Animations Based on Events
|
||||
|
||||
You can trigger animations in response to various user events like `click`, `hover`, or `focus`. This makes the animations interactive, responding to user actions.
|
||||
|
||||
### Example: Button Click Animation
|
||||
|
||||
Let’s animate a button's background color when it’s clicked:
|
||||
|
||||
```js
|
||||
import button from '/elements/button.js';
|
||||
|
||||
export default class AnimatedButton extends button {
|
||||
|
||||
label = "Click Me";
|
||||
|
||||
async click() {
|
||||
// Create a new animation
|
||||
this.animation = this.createAnimation("buttonClick");
|
||||
|
||||
var key = this.animation.createKeyFrame(0);
|
||||
key.setProperty("background-color", "#03a9f4"); // Initial color
|
||||
|
||||
key = this.animation.createKeyFrame(100);
|
||||
key.setProperty("background-color", "#f92672"); // Final color
|
||||
|
||||
// Play the animation for 2 seconds
|
||||
await this.animation.play("2s");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When the user clicks the button, the `click()` method triggers the animation that changes the background color from **blue** (`#03a9f4`) to **red** (`#f92672`).
|
||||
|
||||
---
|
||||
|
||||
## 5. Sequential and Chained Animations
|
||||
|
||||
You can also chain animations together to create more complex effects. For instance, you can have one animation complete before the next one starts.
|
||||
|
||||
### Example: Sequential Animations
|
||||
|
||||
```js
|
||||
class SequentialAnimations {
|
||||
|
||||
element = document.createElement("div");
|
||||
width = 100;
|
||||
height = 100;
|
||||
margin = 20;
|
||||
|
||||
create() {
|
||||
// Animation 1: Fade in
|
||||
this.animation1 = this.createAnimation("fadeIn");
|
||||
var key = this.animation1.createKeyFrame(0);
|
||||
key.setProperty("opacity", "0");
|
||||
|
||||
key = this.animation1.createKeyFrame(100);
|
||||
key.setProperty("opacity", "1");
|
||||
|
||||
// Animation 2: Scale up
|
||||
this.animation2 = this.createAnimation("scaleUp");
|
||||
key = this.animation2.createKeyFrame(0);
|
||||
key.setProperty("transform", "scale(0.5)");
|
||||
|
||||
key = this.animation2.createKeyFrame(100);
|
||||
key.setProperty("transform", "scale(1)");
|
||||
}
|
||||
|
||||
async click() {
|
||||
// Play fade-in animation first
|
||||
await this.animation1.play("2s");
|
||||
|
||||
// After fade-in, play scale-up animation
|
||||
await this.animation2.play("2s");
|
||||
|
||||
alert("Both animations are complete!");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Explanation:
|
||||
|
||||
* **Sequential Execution**: The `await` ensures that the animations play one after another. First, the **fade-in** animation plays, followed by the **scale-up** animation.
|
||||
* **Multiple Animations**: The keyframes for each animation are created independently, and their respective properties change over time.
|
||||
|
||||
---
|
||||
|
||||
## 6. Animating Transitions Between States
|
||||
|
||||
Animations are often used for transitioning between different **states** of an application, such as showing or hiding a panel, or transitioning between pages.
|
||||
|
||||
### Example: Sliding In a Panel
|
||||
|
||||
```js
|
||||
import panel from '/elements/panel.js';
|
||||
|
||||
export default class SlidePanel extends panel {
|
||||
isOpen = false;
|
||||
|
||||
async click() {
|
||||
if (!this.isOpen) {
|
||||
// Slide in animation
|
||||
this.animation = this.createAnimation("slideIn");
|
||||
var key = this.animation.createKeyFrame(0);
|
||||
key.setProperty("transform", "translateX(100%)");
|
||||
|
||||
key = this.animation.createKeyFrame(100);
|
||||
key.setProperty("transform", "translateX(0)");
|
||||
|
||||
await this.animation.play("0.5s");
|
||||
this.isOpen = true;
|
||||
} else {
|
||||
// Slide out animation
|
||||
this.animation = this.createAnimation("slideOut");
|
||||
var key = this.animation.createKeyFrame(0);
|
||||
key.setProperty("transform", "translateX(0)");
|
||||
|
||||
key = this.animation.createKeyFrame(100);
|
||||
key.setProperty("transform", "translateX(100%)");
|
||||
|
||||
await this.animation.play("0.5s");
|
||||
this.isOpen = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Steps:
|
||||
|
||||
1. **Slide In**: The panel slides in from the right (by transitioning `translateX(100%)` to `translateX(0)`).
|
||||
2. **Slide Out**: When clicked again, the panel slides out of view (by transitioning `translateX(0)` to `translateX(100%)`).
|
||||
3. **Interactive**: The `isOpen` flag determines whether to slide in or slide out, toggling the state.
|
||||
|
||||
---
|
||||
|
||||
## 7. Conclusion
|
||||
|
||||
Unify’s animation system provides a **flexible, declarative approach** to animating UI elements. Whether you're animating simple property changes or complex transitions between multiple states, you can easily add animations to your UI.
|
||||
|
||||
Key features:
|
||||
|
||||
* **Multiple keyframes** to animate multiple properties simultaneously.
|
||||
* **Event-driven animations** triggered by user interactions (e.g., clicks, page loads).
|
||||
* **Sequential and parallel animations** to create complex visual effects.
|
||||
* **Smooth transitions** for UI states like opening/closing panels or transitioning between pages.
|
||||
|
||||
By leveraging Unify’s animation system, you can craft engaging and interactive user experiences with minimal effort.
|
||||
215
Permissions.md
Normal file
215
Permissions.md
Normal file
@@ -0,0 +1,215 @@
|
||||
|
||||
# Permissions: Groups, Scopes, Authorization Rules
|
||||
|
||||
In Unify, the **permissions system** is designed to give you full control over who can **read**, **write**, or **delete** data, while allowing you to enforce security at various levels of the application.
|
||||
|
||||
Unify offers a **flexible**, **declarative** approach to managing permissions across different objects, allowing you to define **authorization rules** based on:
|
||||
|
||||
* **User Groups** (e.g., admin, guest, member)
|
||||
* **Scopes** (e.g., a column, a table, or an entire resource)
|
||||
* **Actions** (e.g., READ, WRITE, DELETE)
|
||||
|
||||
## 1. Groups: Defining User Roles
|
||||
|
||||
In Unify, **Groups** represent user roles, and each group has certain **permissions** associated with it. These groups can be used to control access at various levels, such as:
|
||||
|
||||
* Viewing data
|
||||
* Editing data
|
||||
* Deleting data
|
||||
|
||||
### Example: Defining Groups
|
||||
|
||||
```js
|
||||
import groups from '/user/group/user.group.permission.js';
|
||||
|
||||
export default class user extends table {
|
||||
|
||||
username = new username();
|
||||
email = new email();
|
||||
groups = new groups(); // Associate user with groups
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
### Common Use Case
|
||||
|
||||
For instance, you might have:
|
||||
|
||||
* **Visitor**: Can only **read** public information
|
||||
* **Member**: Can **read** and **write** content
|
||||
* **Admin**: Can perform all actions, including **delete**
|
||||
|
||||
You can easily enforce these permissions using:
|
||||
|
||||
```js
|
||||
permission() {
|
||||
this.allow(groups.visitor, "READ"); // Visitors can read
|
||||
this.allow(groups.member, "READ"); // Members can read
|
||||
this.allow(groups.member, "WRITE"); // Members can write
|
||||
this.allow(groups.admin, "READ"); // Admins can read
|
||||
this.allow(groups.admin, "WRITE"); // Admins can write
|
||||
this.allow(groups.admin, "DELETE"); // Admins can delete
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Scopes: Fine-Grained Permission Control
|
||||
|
||||
**Scopes** allow you to define permissions not just for entire tables or models, but for **specific fields** or **columns** within those models.
|
||||
|
||||
For example, a user might be allowed to **read** all the data but only **edit** a certain field, such as a profile description or a specific product attribute.
|
||||
|
||||
### Example: Scope-Based Permissions
|
||||
|
||||
```js
|
||||
permission() {
|
||||
// Allow "member" group to read all users but only update email
|
||||
this.allow(groups.member, "READ");
|
||||
this.allow(groups.member, "WRITE", "email"); // Allow update on email only
|
||||
this.allow(groups.admin, "READ");
|
||||
this.allow(groups.admin, "WRITE");
|
||||
this.allow(groups.admin, "DELETE");
|
||||
}
|
||||
```
|
||||
|
||||
In this example:
|
||||
|
||||
* **Members** can **read** all data but only **update** the `email` field.
|
||||
* **Admins** have full access (can **read**, **write**, and **delete** all fields).
|
||||
|
||||
You can also control **views** or **actions** like **deleting** based on a particular column or resource.
|
||||
|
||||
---
|
||||
|
||||
## 3. Authorization Rules: Controlling Access
|
||||
|
||||
**Authorization rules** are how you declare which groups have what types of access to your tables, fields, or methods. These rules govern **what actions** a group can perform on **what objects**.
|
||||
|
||||
You can define rules for specific actions such as:
|
||||
|
||||
* **READ**: View data
|
||||
* **WRITE**: Modify data
|
||||
* **DELETE**: Remove data
|
||||
|
||||
Each object can implement an associated **`permission()`** method to specify which group can perform what action on the object.
|
||||
|
||||
### Example: Implementing Authorization Rules
|
||||
|
||||
```js
|
||||
export default class news extends table {
|
||||
|
||||
title = new title();
|
||||
body = new body();
|
||||
|
||||
permission() {
|
||||
this.allow(groups.visitor, "READ"); // Visitors can read the news
|
||||
this.allow(groups.member, "WRITE"); // Members can write/edit news
|
||||
this.allow(groups.admin, "READ"); // Admins can read
|
||||
this.allow(groups.admin, "WRITE"); // Admins can write
|
||||
this.allow(groups.admin, "DELETE"); // Admins can delete
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Implementing Permissions in `node` Methods
|
||||
|
||||
In addition to controlling UI elements like columns, you may want to enforce permissions when executing actions on the server (such as creating, updating, or deleting records). This is where **manual permission checks** come into play.
|
||||
|
||||
### Example: Using Permissions in `node` Methods
|
||||
|
||||
```js
|
||||
node async deleteRow(client) {
|
||||
const user = client.user;
|
||||
|
||||
// Check if the user has permission to delete
|
||||
if (!user.hasPermission("DELETE_NEWS")) {
|
||||
throw new Error("Permission denied: User cannot delete");
|
||||
}
|
||||
|
||||
this.delete(); // Perform the delete action
|
||||
}
|
||||
```
|
||||
|
||||
In this example, the `deleteRow` method checks whether the **current user** has permission to delete the record. If they do not, it throws an error and prevents the action.
|
||||
|
||||
---
|
||||
|
||||
## 5. Permissions with Collections
|
||||
|
||||
Since collections may contain multiple rows (or objects), you may also need to control access at the **collection level**. This allows you to define permissions that apply across the entire dataset.
|
||||
|
||||
### Example: Permissions for Collection
|
||||
|
||||
```js
|
||||
export default class newsListTableBody extends renderCollection, gridViewBody {
|
||||
|
||||
permission() {
|
||||
this.allow(groups.visitor, "READ"); // Visitors can read all news in the collection
|
||||
this.allow(groups.member, "WRITE"); // Members can write new news items
|
||||
this.allow(groups.admin, "DELETE"); // Admins can delete news items
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This ensures that each row in the collection respects the user’s permissions when they try to **view**, **create**, **update**, or **delete** entries.
|
||||
|
||||
---
|
||||
|
||||
## 6. Combining Permissions with UI
|
||||
|
||||
In Unify, you **don't** have to manually manage every UI element (such as showing or hiding a button) based on permissions. You can use the `permission()` method to dynamically control UI elements' visibility and accessibility by the roles assigned.
|
||||
|
||||
For instance:
|
||||
|
||||
```js
|
||||
node async click() {
|
||||
if (this.user.hasPermission("EDIT_NEWS")) {
|
||||
this.showEditForm();
|
||||
} else {
|
||||
throw new Error("Permission denied: Cannot edit news");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can pair the **permissions check** with UI changes to ensure users can only interact with elements they are authorized to use.
|
||||
|
||||
---
|
||||
|
||||
## 7. Custom Authorization Logic
|
||||
|
||||
You can also write custom authorization logic, such as role hierarchies (where one group has broader permissions than another).
|
||||
|
||||
### Example: Custom Authorization
|
||||
|
||||
```js
|
||||
node async deleteRow(client) {
|
||||
const user = client.user;
|
||||
|
||||
// Only admin or user who created the news can delete it
|
||||
if (!(user.hasPermission("DELETE_NEWS") || this.createdBy === user.id)) {
|
||||
throw new Error("Permission denied: User cannot delete this news item");
|
||||
}
|
||||
|
||||
this.delete(); // Proceed to delete if authorized
|
||||
}
|
||||
```
|
||||
|
||||
This custom rule checks whether the **current user** is an **admin** or the **creator** of the news item before allowing them to delete it.
|
||||
|
||||
---
|
||||
|
||||
## Summary of Permissions System
|
||||
|
||||
| Element | Description |
|
||||
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Groups** | Roles that define the access level of a user (e.g., Admin, Member, Visitor) |
|
||||
| **Scopes** | Granular permissions tied to specific fields, columns, or actions within a model |
|
||||
| **Authorization Rules** | Methods (`permission()`) where groups are assigned specific actions (READ, WRITE, DELETE) for objects, fields, or collections |
|
||||
| **Custom Logic** | Developers can write custom authorization logic for complex rules like ownership or hierarchical access |
|
||||
|
||||
Unify's permission system is flexible, allowing developers to define, check, and enforce rules throughout the application — from **UI visibility** to **backend operations** like SQL queries and method executions.
|
||||
|
||||
|
||||
64
Property_Binding.md
Normal file
64
Property_Binding.md
Normal file
@@ -0,0 +1,64 @@
|
||||
|
||||
|
||||
### Automatic Rendering and Property Binding in Unify
|
||||
|
||||
In Unify, you **define properties** on your objects, and the framework automatically **creates the associated DOM elements**, applies **styles**, and **binds the properties** without you needing to manually manipulate the DOM. This approach significantly reduces boilerplate code, making it easier and faster to build dynamic user interfaces.
|
||||
|
||||
#### Key Concepts:
|
||||
|
||||
1. **Automatic Element Creation**:
|
||||
When you define a Unify object, like a `panel`, the framework automatically generates the corresponding HTML element for you. You don't need to manually call `document.createElement()` or manage the element's attributes.
|
||||
|
||||
2. **Property Binding**:
|
||||
The properties you define (such as `height`, `width`, or `text`) are automatically reflected in the rendered DOM element. For instance, if you set the `height` property of a `panel` object, Unify ensures that the corresponding HTML element's height is updated accordingly.
|
||||
|
||||
3. **No Need for Render Methods**:
|
||||
Unlike traditional frameworks where you need to explicitly define a `render()` method to apply changes to the DOM, Unify handles this for you. Simply updating the properties of a Unify object triggers the necessary updates in the DOM without needing extra code.
|
||||
|
||||
4. **Dynamic Updates**:
|
||||
Whenever a property is changed, such as adjusting the `height` or `text` of an element, Unify automatically re-renders the element to reflect these changes. There's no need to manually call DOM manipulation methods like `element.style.height` or `element.innerText`.
|
||||
|
||||
#### Example:
|
||||
|
||||
Here’s an example demonstrating how Unify makes it easy to manage UI elements:
|
||||
|
||||
```js
|
||||
class panel {
|
||||
// Define initial properties
|
||||
height = 100;
|
||||
width = 200;
|
||||
margin = 20;
|
||||
text = "Initial Text";
|
||||
|
||||
// Modify properties dynamically
|
||||
click() {
|
||||
this.height = 300; // Change height
|
||||
this.width = 400; // Change width
|
||||
this.text = "Updated Text"; // Change text content
|
||||
}
|
||||
}
|
||||
|
||||
// Create an instance of the panel
|
||||
const myPanel = new panel();
|
||||
```
|
||||
|
||||
### How It Works:
|
||||
|
||||
1. **No Manual DOM Updates**: You don’t need to manually create elements or apply styles. Unify automatically creates an HTML element for your `panel` and applies the `height`, `width`, and `text` properties to it.
|
||||
|
||||
2. **Automatic Synchronization**: When you modify properties like `height`, `width`, or `text`, Unify ensures that the corresponding DOM element is updated to reflect these changes without requiring additional code.
|
||||
|
||||
3. **Simplified UI Development**: This approach makes it easier to build dynamic, interactive UIs. You simply update the object’s properties, and Unify handles the underlying DOM updates.
|
||||
|
||||
4. **Declarative Approach**: By defining properties on objects, you declaratively specify the state of your UI, and Unify takes care of the rendering and synchronization for you.
|
||||
|
||||
### Benefits:
|
||||
|
||||
* **Less Boilerplate Code**: Since Unify automatically binds properties to DOM elements, there's no need for repetitive code to manually update styles or text.
|
||||
* **Faster Development**: By removing the need for manual DOM manipulation, you can focus on defining the state and behavior of your components.
|
||||
* **Automatic Synchronization**: Changes to object properties are automatically reflected in the UI without any extra code to update the DOM.
|
||||
|
||||
---
|
||||
|
||||
This approach streamlines the process of building dynamic UIs and ensures that your objects and the DOM are always in sync, reducing the likelihood of errors and improving maintainability. With Unify, you focus on defining the state of your UI, and the framework takes care of the rendering for you.
|
||||
|
||||
214
Readme.md
Normal file
214
Readme.md
Normal file
@@ -0,0 +1,214 @@
|
||||
|
||||
|
||||
# Unify Developer Manual
|
||||
|
||||
### Build full applications with a single, unified codebase.
|
||||
|
||||
Unify is a full-stack application framework designed to remove the accidental complexity of modern web development. Instead of hand-wiring HTTP requests, state management, SQL queries, UI updates, permissions, and caching — Unify turns each **domain object** into a **live application object** that exists simultaneously on the server and client.
|
||||
|
||||
You write your application *once*.
|
||||
Unify takes care of syncing it everywhere it needs to be.
|
||||
|
||||
---
|
||||
|
||||
## Why Unify?
|
||||
|
||||
Most web apps are assembled from disconnected technologies:
|
||||
React + REST + ORM + SQL + State Manager + WebSockets + UI Framework + Access Control…
|
||||
|
||||
Each layer requires boilerplate to keep everything in sync:
|
||||
|
||||
* serialize → send → parse → update → re-render → validate permissions
|
||||
* write SQL + write endpoints + write frontend state logic for the same entity
|
||||
|
||||
Unify eliminates these seams.
|
||||
|
||||
> **One class = data + logic + UI + permissions + real-time behavior**
|
||||
|
||||
The framework:
|
||||
|
||||
* turns your class into a database table (server)
|
||||
* generates UI components from that class (client)
|
||||
* syncs all changes via a structured WebSocket protocol
|
||||
* applies permissions automatically based on user role
|
||||
* integrates filtering, joins, pagination at the SQL level
|
||||
* caches and updates only changed fields
|
||||
|
||||
You focus on the domain model.
|
||||
Unify handles the rest.
|
||||
|
||||
---
|
||||
|
||||
|
||||
### Why Unify Is AI-Optimized
|
||||
|
||||
Unify applications are self-describing graphs.
|
||||
The object structure defines:
|
||||
|
||||
UI layout
|
||||
|
||||
Backend relationships
|
||||
|
||||
Permissions
|
||||
|
||||
Sync rules
|
||||
|
||||
Navigation
|
||||
|
||||
A human developer may lose track of where objects live in the graph.
|
||||
|
||||
But AI can:
|
||||
|
||||
infer full graph topology
|
||||
|
||||
find optimal traversal paths
|
||||
|
||||
safely modify relationships
|
||||
|
||||
rewrite UI interactions without breaking structure
|
||||
|
||||
This means Unify enables:
|
||||
|
||||
AI-generated UI actions
|
||||
|
||||
AI-guided refactoring
|
||||
|
||||
AI-assisted CRUD creation
|
||||
|
||||
Automated join discovery
|
||||
|
||||
Intelligent navigation handling
|
||||
|
||||
|
||||
|
||||
## What does development feel like?
|
||||
|
||||
Example: define a `News` class
|
||||
|
||||
```javascript
|
||||
export default class news extends table {
|
||||
title = new text("Title");
|
||||
body = new text("Message");
|
||||
price = new number("Price");
|
||||
|
||||
permission() {
|
||||
this.allow(groups.visitor, "READ");
|
||||
this.allow(groups.admin, "WRITE");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And a list view:
|
||||
|
||||
```javascript
|
||||
export default class newsListTable extends gridView {
|
||||
header = new newsListTableHeader();
|
||||
body = new newsListTableBody(newsListItem, new collection(news));
|
||||
}
|
||||
```
|
||||
|
||||
You don’t manually:
|
||||
|
||||
* write SQL queries
|
||||
* define REST endpoints
|
||||
* build WebSocket handlers
|
||||
* implement CRUD actions
|
||||
* code client state syncing
|
||||
* generate UI markup
|
||||
|
||||
Unify does all of that from your object graph.
|
||||
|
||||
---
|
||||
|
||||
## Architecture: What’s inside
|
||||
|
||||
| Layer | Responsibility |
|
||||
| ---------------------- | ------------------------------------------ |
|
||||
| **Domain Objects** | Single class shared by server + client |
|
||||
| **Render Collections** | Auto-loaded lists with pagination + search |
|
||||
| **Table Controller** | Database operations + joins |
|
||||
| **Socket Controller** | Real-time communication layer |
|
||||
| **UI Runtime** | Live elements, animations, events |
|
||||
| **Permission Engine** | Automatic security per object/action |
|
||||
|
||||
This creates a **Behavioral ORM**:
|
||||
|
||||
* objects contain their own server logic
|
||||
* queries are automatically composed from UI actions
|
||||
* users see only what they are permitted to interact with
|
||||
|
||||
---
|
||||
|
||||
## Why does this matter?
|
||||
|
||||
Modern software complexity isn’t caused by features — it’s caused by **glue code**.
|
||||
For every feature, you implement it 3–6 times: backend models, database schema, API routes, frontend state, UI bindings…
|
||||
|
||||
Unify collapses those layers into **one source of truth**.
|
||||
|
||||
The result:
|
||||
|
||||
* drastically fewer bugs
|
||||
* ultra-fast iteration cycles
|
||||
* real-time UX without extra work
|
||||
* secure-by-construction access rules
|
||||
* smaller teams can ship full apps
|
||||
|
||||
---
|
||||
|
||||
## Current focus
|
||||
|
||||
The goal of this release is to give developers a working demonstration of:
|
||||
|
||||
* full-stack sync with no REST boilerplate
|
||||
* real-time UI updates
|
||||
* built-in permissions
|
||||
* SQL searching / joining from the UI
|
||||
* cross-platform dynamic UI rendering (Windows / Mac / Web)
|
||||
|
||||
The included **News Application** shows:
|
||||
|
||||
* search/filter with SQL operators (`LIKE`, `OR`, `>=`)
|
||||
* item detail transitions + CRUD dialogs
|
||||
* login + role-based access
|
||||
* pagination
|
||||
* dynamic view/layout behavior
|
||||
|
||||
---
|
||||
|
||||
## Who is Unify for?
|
||||
|
||||
✔ Full-stack developers
|
||||
✔ Product developers who hate glue code
|
||||
✔ Teams who want to ship faster
|
||||
✔ Anyone who believes software should be simpler
|
||||
|
||||
---
|
||||
|
||||
## Status
|
||||
|
||||
Unify is actively developed and evolving.
|
||||
Contributions, feedback, and architectural discussion are welcome.
|
||||
|
||||
> Help define the future of unified application development.
|
||||
|
||||
---
|
||||
|
||||
## Get started
|
||||
|
||||
The source includes:
|
||||
|
||||
* `unify_source/` – the core framework
|
||||
* `unify_application/` – working reference application
|
||||
|
||||
Clone and run the app to see the architecture in motion:
|
||||
|
||||
```
|
||||
git clone https://.../unify
|
||||
cd unify_application
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
|
||||
Documentation and complete API references are coming.
|
||||
|
||||
212
Rendering.md
Normal file
212
Rendering.md
Normal file
@@ -0,0 +1,212 @@
|
||||
|
||||
|
||||
# Rendering Data: Render Collections, Lists & Pagination
|
||||
|
||||
In Unify, data-driven interfaces, such as tables, lists, or collections, are rendered through a powerful concept: **Render Collections**.
|
||||
|
||||
A **Render Collection** is a UI component that displays rows of data, with a complete lifecycle from querying the backend to rendering the results, all while maintaining a declarative, JavaScript-centric approach.
|
||||
|
||||
## 1. Define a Render Collection
|
||||
|
||||
A render collection consists of two key components:
|
||||
|
||||
* **The Row Renderer**: A component that defines how each row is visually rendered.
|
||||
* **The Collection**: A component that defines the data source and binds to a backend Table (like `news`, `users`, etc.).
|
||||
|
||||
### Example: Defining a Render Collection
|
||||
|
||||
```js
|
||||
export default class newsListTable extends gridView {
|
||||
|
||||
header = new newsListTableHeader();
|
||||
|
||||
body = new newsListTableBody(
|
||||
newsListItem, // Row Renderer
|
||||
new collection(news) // Backend Data Binding
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
In this example:
|
||||
|
||||
* **`newsListItem`** is the row component used to render individual items.
|
||||
* **`new collection(news)`** binds to the `news` Table on the backend, providing the data to be displayed.
|
||||
|
||||
### How it works:
|
||||
|
||||
* The **Collection** defines the data.
|
||||
* The **Row Renderer** defines how to display each individual row.
|
||||
|
||||
---
|
||||
|
||||
## 2. Syncing Data — Client-Server Integration
|
||||
|
||||
Unify makes sure the **data remains synced** across the client and the backend. You can fetch data on-demand and refresh it easily with the `.sync()` method:
|
||||
|
||||
```js
|
||||
await this.body.sync();
|
||||
```
|
||||
|
||||
When the data is synced, the following happens:
|
||||
|
||||
1. The query is sent to the server (via `node async filterSearch()`).
|
||||
2. The server executes the query and returns the rows.
|
||||
3. The rows are parsed and displayed in the UI via the **Row Renderer**.
|
||||
|
||||
If the collection is already **pre-bound** to a table with an `id`, calling `.sync()` will also refresh that specific object’s details.
|
||||
|
||||
---
|
||||
|
||||
## 3. Pagination — Developer Controlled
|
||||
|
||||
Pagination in Unify is **developer-controlled**. Unlike typical frameworks that handle pagination automatically, you have full control over how the **limit**, **offset**, and **searching** are applied.
|
||||
|
||||
### Pagination Example:
|
||||
|
||||
```js
|
||||
async update(updatePagination = true) {
|
||||
if (updatePagination) {
|
||||
this.page = 0;
|
||||
}
|
||||
|
||||
// Filtering and pagination logic
|
||||
this.numberOfPages = await this.filterSearch(
|
||||
this.searchType,
|
||||
this.searchTerm,
|
||||
this.sort,
|
||||
this.direction,
|
||||
this.limit,
|
||||
this.page
|
||||
);
|
||||
|
||||
await this.sync();
|
||||
|
||||
if (updatePagination) {
|
||||
this.parents("newsItemPage").tableControl.pagination.create();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this case:
|
||||
|
||||
1. **`filterSearch()`** constructs the filter for the query and is called to fetch the rows.
|
||||
2. After filtering, **pagination** is managed manually.
|
||||
3. **`sync()`** then updates the UI with the results.
|
||||
|
||||
This gives you control over **pagination logic** and how the list is managed.
|
||||
|
||||
---
|
||||
|
||||
## 4. Filtering and Searching — Declarative Querying
|
||||
|
||||
In Unify, **filtering and searching** are handled using **logical expressions** that are parsed on the server side. You construct the queries using Unify's SQL helpers:
|
||||
|
||||
```js
|
||||
import OR from '/unify/sql/OR.js';
|
||||
import LIKE from '/unify/sql/LIKE.js';
|
||||
import GREATER from '/unify/sql/GREATER_OR_EQUAL.js';
|
||||
import SMALLER from '/unify/sql/SMALLER_OR_EQUAL.js';
|
||||
|
||||
node async filterSearch(searchType, searchTerm, order, direction, limit, page) {
|
||||
const filter = this.getFilter();
|
||||
|
||||
switch (searchType) {
|
||||
case 0:
|
||||
filter.search = OR(
|
||||
OR(LIKE(filter.title, searchTerm), LIKE(filter.comments.body, searchTerm)),
|
||||
LIKE(filter.body, searchTerm)
|
||||
);
|
||||
break;
|
||||
case 1:
|
||||
filter.search = GREATER(filter.price, searchTerm);
|
||||
break;
|
||||
case 2:
|
||||
filter.search = SMALLER(filter.price, searchTerm);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!searchTerm) filter.search = false;
|
||||
|
||||
filter.order = filter[order];
|
||||
filter.direction = direction === "desc" ? "desc" : "asc";
|
||||
|
||||
filter.limit = parseInt(limit);
|
||||
filter.from = parseInt(page * limit);
|
||||
|
||||
await this.get(); // Fetch results
|
||||
|
||||
const numberOfPages = Math.ceil(this.rows.length / limit);
|
||||
return numberOfPages;
|
||||
}
|
||||
```
|
||||
|
||||
### Key Points:
|
||||
|
||||
* **Logical operators** like `OR`, `AND`, and `LIKE` are used to build declarative filters.
|
||||
* Filters are executed directly **on the server**. The objects are **parsed as-is**, without the need for string-based queries or manual SQL building.
|
||||
* **Pagination and sorting** logic is fully under the developer’s control.
|
||||
|
||||
The **query-building** helpers (`LIKE`, `GREATER`, `SMALLER`) are parsed directly by the server. They allow you to declaratively specify complex queries in JavaScript, all while Unify handles the logic and results.
|
||||
|
||||
---
|
||||
|
||||
## 5. Permissions — Controlling Access to Data
|
||||
|
||||
Just like Unify Table objects, **Render Collections** have a permission system that controls who can **read**, **write**, or **delete** data. For example:
|
||||
|
||||
```js
|
||||
permission() {
|
||||
this.allow(groups.visitor, "READ");
|
||||
this.allow(groups.member, "READ");
|
||||
this.allow(groups.admin, "READ");
|
||||
}
|
||||
```
|
||||
|
||||
Permissions can control:
|
||||
|
||||
* Which rows are visible to different user roles
|
||||
* Which fields inside those rows are visible or editable
|
||||
|
||||
It’s important to note that **filtering** does not automatically enforce permissions. You must explicitly **validate** access rights to each table, column, or row.
|
||||
|
||||
For example, before executing an action like `deleteRow()`, you must check the user’s permissions:
|
||||
|
||||
```js
|
||||
node async deleteRow(client) {
|
||||
const user = client.user;
|
||||
|
||||
if (!user.hasPermission("DELETE_NEWS")) {
|
||||
throw new Error("Permission denied");
|
||||
}
|
||||
|
||||
this.delete();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Lifecycle Summary
|
||||
|
||||
| Action | Happens Where | Implemented by |
|
||||
| --------------------------- | ------------------------ | -------------- |
|
||||
| Configure UI of list | Client | Developer |
|
||||
| Construct query logic | Shared (Client + Server) | Developer |
|
||||
| Execute SQL on server | Server | Unify |
|
||||
| Deserialize objects | Server → Client | Unify |
|
||||
| Render rows in the UI | Client | Unify |
|
||||
| Handle permissions for rows | Server | Developer |
|
||||
|
||||
Unify abstracts away complex data handling. The **UI** remains declarative and **directly synced** with the **backend**, ensuring **zero glue code** between database and frontend.
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
Unify’s **Render Collections** provide a simple, declarative way to build **dynamic, paginated, filtered lists**:
|
||||
|
||||
1. The **backend logic** remains completely abstracted (no REST APIs, no ORM configuration).
|
||||
2. The **client UI** renders the data based on **filter and query objects**.
|
||||
3. The **permissions and security** of the data are automatically respected with clear separation between **frontend** and **backend** logic.
|
||||
|
||||
The system gives you **total control** over pagination, sorting, searching, and permissions, while Unify handles the **syncing**, **UI updates**, and **backend query execution** for you.
|
||||
|
||||
163
Tables.md
Normal file
163
Tables.md
Normal file
@@ -0,0 +1,163 @@
|
||||
Here is a fully rewritten version of the entire **Tables + Columns + Floating Mapping** section — accurate, simplified, and without over-emphasizing the “floating” term, while still explaining why it matters.
|
||||
|
||||
---
|
||||
|
||||
## Tables & Columns
|
||||
|
||||
### Automatic SQL Mapping from UI Components
|
||||
|
||||
In Unify, **your UI defines your data model**.
|
||||
|
||||
Every data entity in an application is a **table object**:
|
||||
|
||||
```js
|
||||
import table from '/unify/table.js';
|
||||
|
||||
export default class user extends table {
|
||||
|
||||
username = new username(); // column
|
||||
email = new email(); // column
|
||||
groups = new groups(); // relation
|
||||
}
|
||||
```
|
||||
|
||||
Any child object that extends `column` is automatically treated as a **database column** belonging to its nearest table ancestor.
|
||||
|
||||
There is no need to write schemas, migrations, or model config files.
|
||||
Unify inspects the structure at runtime and understands:
|
||||
|
||||
* Table name → class name (`user`)
|
||||
* Column names → field names (`username`, `email`, …)
|
||||
* Relationships → nested collections
|
||||
* Permissions → defined on the table
|
||||
|
||||
No duplication. No ORM setup.
|
||||
Your UI code **is** your data model.
|
||||
|
||||
---
|
||||
|
||||
### Columns Can Live Anywhere in the UI Tree
|
||||
|
||||
Columns do not need to be direct children of the table.
|
||||
|
||||
You can design your UI freely:
|
||||
|
||||
```js
|
||||
export default class userEditPanel extends panel {
|
||||
accountPanel = new accountPanel();
|
||||
contactPanel = new contactPanel();
|
||||
}
|
||||
|
||||
class accountPanel extends panel {
|
||||
username = new username(); // column
|
||||
}
|
||||
|
||||
class contactPanel extends panel {
|
||||
email = new email(); // column
|
||||
}
|
||||
```
|
||||
|
||||
Unify automatically discovers these fields because they appear **under a table instance** in the object tree.
|
||||
|
||||
So you are free to:
|
||||
|
||||
* Split a screen into panels
|
||||
* Reuse complex component hierarchies
|
||||
* Move UI structure without refactoring SQL code
|
||||
|
||||
This enables **UI-first design** without breaking the data layer.
|
||||
|
||||
---
|
||||
|
||||
### CRUD Without CRUD Code
|
||||
|
||||
Unify provides two primary data operations:
|
||||
|
||||
| Action | Developer code | Result |
|
||||
| ------ | ------------------- | ----------------------------------- |
|
||||
| Save | `await user.save()` | INSERT or UPDATE depending on `id` |
|
||||
| Load | `await user.sync()` | SELECT + populate all child columns |
|
||||
|
||||
Example: Editing a profile
|
||||
|
||||
```js
|
||||
async click() {
|
||||
const form = this.parents('userEdit');
|
||||
await form.save(); // automatically writes username & email
|
||||
form.hide();
|
||||
}
|
||||
```
|
||||
|
||||
And for loading:
|
||||
|
||||
```js
|
||||
user.id = 42;
|
||||
await user.sync(); // loads username/email into the UI automatically
|
||||
```
|
||||
|
||||
No setters.
|
||||
No binding boilerplate.
|
||||
No DTOs.
|
||||
No form libraries.
|
||||
|
||||
The table object is both:
|
||||
|
||||
* Data source
|
||||
* UI container
|
||||
* Binding layer
|
||||
|
||||
---
|
||||
|
||||
### Why This Matters
|
||||
|
||||
| Traditional Full-Stack | With Unify |
|
||||
| --------------------------- | -------------------------- |
|
||||
| Write database schema | Skip — inferred |
|
||||
| Write backend model | Skip — inferred |
|
||||
| Write API controllers | Skip — RPC auto-generation |
|
||||
| Write validation logic | Built into column objects |
|
||||
| Build UI → manually connect | UI *is* the data model |
|
||||
| Keep everything in sync | `.sync()` + `.save()` |
|
||||
|
||||
This eliminates the friction between backend + frontend and replaces it with one unified codebase.
|
||||
|
||||
---
|
||||
|
||||
### Minimal Mental Model
|
||||
|
||||
> A **table** object represents a database row.
|
||||
> Any **column** object inside that table’s UI subtree automatically becomes a database column.
|
||||
> `sync()` loads data into the UI.
|
||||
> `save()` writes UI data back to SQL.
|
||||
|
||||
Nothing more is needed to build and ship real applications.
|
||||
|
||||
---
|
||||
|
||||
### Example Recap
|
||||
|
||||
```js
|
||||
user.id = 7;
|
||||
await user.sync(); // load existing row → UI updates
|
||||
|
||||
user.username.value = "kaj";
|
||||
await user.save(); // update DB row with new values
|
||||
```
|
||||
|
||||
One line to fetch, one line to commit.
|
||||
|
||||
---
|
||||
|
||||
If you'd like, the next chapter can flow directly into **Render Collections & Lists**, since they demonstrate the same philosophy at scale:
|
||||
|
||||
* UI defines queries (pagination, filtering)
|
||||
* Changes propagate automatically across the app
|
||||
|
||||
Should I continue with:
|
||||
|
||||
A) Render Collections + Filtering/Searching
|
||||
B) Permissions (groups, scopes, READ/WRITE/DELETE)
|
||||
C) Routing + Animated Page State
|
||||
D) Server RPC Communication Model (socket commands)
|
||||
|
||||
Just tell me which one comes next.
|
||||
65
Why.md
Normal file
65
Why.md
Normal file
@@ -0,0 +1,65 @@
|
||||
### 1. **Full-Stack Integration with Minimal Effort**
|
||||
|
||||
Unify offers a **high-level abstraction** over traditional full-stack frameworks, combining the backend (data models, SQL queries, server-side logic) and frontend (UI rendering, state management, animations) into a single cohesive system. It drastically reduces the need for **manual synchronization** between layers, which is typically a source of complexity in traditional architectures.
|
||||
|
||||
**Value Add**: By using Unify, you're gaining expertise in building applications that **automatically sync** across client and server, without needing to write separate API routes, manage frontend state, or even handle SQL queries manually.
|
||||
|
||||
### 2. **AI Optimization Potential**
|
||||
|
||||
Unify's **self-describing object graph** enables AI to generate code for UI actions, refactoring, CRUD generation, and more. This opens up the potential for **AI-assisted development**, making tasks like optimizing UI behavior, managing state, and writing backend logic much faster.
|
||||
|
||||
**Value Add**: You’re working with a **forward-thinking framework** that embraces AI optimization, placing you at the forefront of innovation in the full-stack development space.
|
||||
|
||||
### 3. **Declarative Approach to Data and UI**
|
||||
|
||||
Unify turns **data models into UI components**. You define your business logic in terms of **classes** and **objects**, and Unify handles the database queries, API calls, state management, and UI bindings.
|
||||
|
||||
**Value Add**: This **declarative model** significantly reduces **boilerplate code** and streamlines the development process. You are not manually wiring complex data flows, which means you can focus more on solving the core problem rather than dealing with infrastructure and repeated patterns of implementation.
|
||||
|
||||
### 4. **Real-Time Sync and Permissions Management**
|
||||
|
||||
Unify automatically syncs data between the client and the server, ensuring that the state is always up-to-date across all layers of the application. It also applies **permissions automatically** based on user roles and object-level access control, ensuring **security** is built into the framework rather than added as an afterthought.
|
||||
|
||||
**Value Add**: You get **real-time syncing** and **permissions enforcement** with minimal effort, which are traditionally complex features to implement. This **reduces the risk of security vulnerabilities** and improves the consistency of user data across applications.
|
||||
|
||||
### 5. **Automatic Query Composition**
|
||||
|
||||
Unify’s system allows **SQL queries to be generated automatically** from UI interactions and object manipulations. Filtering, pagination, and joins are built-in features, which means you don't have to write SQL manually for each interaction. You simply define what you want the data to look like, and Unify handles the query generation.
|
||||
|
||||
**Value Add**: This **eliminates SQL boilerplate** and query-building code, helping you rapidly prototype applications with complex data requirements, saving you significant time on backend development.
|
||||
|
||||
### 6. **Flexible and Scalable UI with Floating Columns**
|
||||
|
||||
The framework allows you to **flexibly design the UI** without worrying about how it will impact the underlying database model. With the concept of **floating columns**, Unify offers **dynamic UI management**, where objects (e.g., panels, tables) can be nested freely, with no disruption to backend data structures.
|
||||
|
||||
**Value Add**: This **enhances your design flexibility** and allows you to **focus on user experience** without the constraints of having to directly map UI components to your database schema. It’s a powerful feature for building **scalable, maintainable UIs**.
|
||||
|
||||
### 7. **Minimal Glue Code and Increased Efficiency**
|
||||
|
||||
Unify eliminates the need for traditional **"glue code"** that connects various parts of an app: backend models, database schema, API routes, and frontend state. Instead, it relies on **automated syncing**, reducing duplication and the potential for bugs.
|
||||
|
||||
**Value Add**: By cutting out this **manual glue code**, you can ship **full applications faster** and with fewer opportunities for errors. Your workflow becomes more **efficient**, focusing solely on defining business logic and UI components.
|
||||
|
||||
---
|
||||
|
||||
### How does it add value to your CV?
|
||||
|
||||
* **Innovative Technology**: Unify is a highly **innovative framework** that addresses many of the challenges full-stack developers face, such as synchronization, permission management, and data handling. Being involved with a framework like this demonstrates your ability to work with cutting-edge technology.
|
||||
|
||||
* **Problem-Solving**: Working with Unify showcases your **problem-solving skills**. You are tackling the problem of **redundant code and complexity** in a novel way, leveraging the power of abstraction to create a more elegant solution for modern web development.
|
||||
|
||||
* **Future-Proofing**: By working with a framework that has the potential to integrate **AI-assisted development**, you’re positioning yourself as someone who is not just keeping up with modern trends but also preparing for **future technologies**.
|
||||
|
||||
* **Reduced Development Time**: Your experience with Unify shows that you can develop and ship complex applications quickly and efficiently, as the framework automates much of the **boilerplate coding** involved in backend and frontend development.
|
||||
|
||||
* **Full-Stack Expertise**: Unify allows you to work across the entire stack, from database to UI to real-time updates, making you a **well-rounded full-stack developer**. This skill is highly valuable in today’s job market.
|
||||
|
||||
* **Security Awareness**: The **automatic permission system** in Unify indicates that you’re mindful of security concerns and can develop applications with **built-in access control**.
|
||||
|
||||
---
|
||||
|
||||
### Conclusion
|
||||
|
||||
Unify, with its **automatic syncing**, **AI optimization potential**, and **declarative approach** to building applications, is an advanced framework that offers significant time-saving benefits. By adding this to your CV, you're showcasing your ability to work with **cutting-edge technologies** that address modern full-stack development challenges.
|
||||
|
||||
If you’re comfortable with the framework’s complexity and confident in its capabilities, it would be **highly beneficial** to include it on your CV, especially if you're targeting positions where efficiency, innovation, and full-stack expertise are valued.
|
||||
69
application/demo/animations/animation.color.js
Normal file
69
application/demo/animations/animation.color.js
Normal file
@@ -0,0 +1,69 @@
|
||||
|
||||
|
||||
import panelRow from '/elements/panel/row.js';
|
||||
|
||||
import label from '/elements/label.js';
|
||||
|
||||
|
||||
class animationBlock{
|
||||
|
||||
width = 100;
|
||||
|
||||
height = 100;
|
||||
|
||||
margin = 20;
|
||||
|
||||
background = "#03a9f4";
|
||||
|
||||
create() {
|
||||
|
||||
this.animation = this.createAnimation("backgroundAnimation");
|
||||
|
||||
var key = this.animation.createKeyFrame( 0 );
|
||||
|
||||
key.setProperty( "background", "#03a9f4" );
|
||||
|
||||
|
||||
var key = this.animation.createKeyFrame( 40 );
|
||||
|
||||
key.setProperty( "background", "#a6e22e" );
|
||||
|
||||
|
||||
|
||||
var key = this.animation.createKeyFrame( 70 );
|
||||
|
||||
key.setProperty( "background", "#f92672" );
|
||||
|
||||
|
||||
|
||||
var key = this.animation.createKeyFrame( 100 );
|
||||
|
||||
key.setProperty( "background", "#03a9f4" );
|
||||
|
||||
}
|
||||
|
||||
async click() {
|
||||
|
||||
this.animation.play("2s");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class rowLabel extends label{
|
||||
|
||||
flex = "1";
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default class row extends panelRow{
|
||||
|
||||
boxWidth = "95%"
|
||||
|
||||
rowLabel = new rowLabel("Background color");
|
||||
|
||||
animationBlock = new animationBlock();
|
||||
|
||||
}
|
||||
79
application/demo/animations/animation.move.js
Normal file
79
application/demo/animations/animation.move.js
Normal file
@@ -0,0 +1,79 @@
|
||||
|
||||
|
||||
class animationBlock{
|
||||
|
||||
width = 100;
|
||||
|
||||
height = 100;
|
||||
|
||||
margin = 20;
|
||||
|
||||
background = "#03a9f4";
|
||||
|
||||
marginTop = 12;
|
||||
|
||||
marginLeft = 12;
|
||||
|
||||
create() {
|
||||
|
||||
|
||||
this.moveAnimation = this.createAnimation("moveAnimation");
|
||||
|
||||
|
||||
var key = this.moveAnimation.createKeyFrame( 0 );
|
||||
|
||||
key.setProperty( "margin-right", 12 );
|
||||
|
||||
|
||||
var key = this.moveAnimation.createKeyFrame( 20 );
|
||||
|
||||
//key.setProperty( "margin-top", 200 );
|
||||
key.setProperty( "margin-right", 40 );
|
||||
|
||||
|
||||
var key = this.moveAnimation.createKeyFrame( 30 );
|
||||
|
||||
//key.setProperty( "margin-top", 500 );
|
||||
key.setProperty( "margin-right", 120 );
|
||||
|
||||
|
||||
var key = this.moveAnimation.createKeyFrame( 50 );
|
||||
|
||||
key.setProperty( "margin-top", 200 );
|
||||
|
||||
|
||||
var key = this.moveAnimation.createKeyFrame( 100 );
|
||||
|
||||
//key.setProperty( "margin-top", 0 );
|
||||
|
||||
}
|
||||
|
||||
async click() {
|
||||
|
||||
this.moveAnimation.play("2s");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
import panelRow from '/elements/panel/row.js';
|
||||
|
||||
import label from '/elements/label.js';
|
||||
|
||||
|
||||
class rowLabel extends label{
|
||||
|
||||
flex = "1";
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default class moveRow extends panelRow{
|
||||
|
||||
boxWidth = "95%"
|
||||
|
||||
rowLabel = new rowLabel("Move");
|
||||
|
||||
animationBlock = new animationBlock();
|
||||
|
||||
}
|
||||
50
application/demo/animations/animation.render.js
Normal file
50
application/demo/animations/animation.render.js
Normal file
@@ -0,0 +1,50 @@
|
||||
|
||||
|
||||
import panelRow from '/elements/panel/row.js';
|
||||
|
||||
import label from '/elements/label.js';
|
||||
|
||||
|
||||
|
||||
class animationBlock{
|
||||
|
||||
width = 100;
|
||||
|
||||
height = 100;
|
||||
|
||||
margin = 20;
|
||||
|
||||
background = "#03a9f4";
|
||||
|
||||
time = 0;
|
||||
|
||||
|
||||
render( ) {
|
||||
|
||||
this.time++;
|
||||
|
||||
this.width = 100 + ( Math.cos( this.time / 100 ) * 100 );
|
||||
|
||||
this.height = 100 + ( Math.sin( this.time / 100 ) * 100 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class rowLabel extends label{
|
||||
|
||||
flex = "1";
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default class row extends panelRow{
|
||||
|
||||
boxWidth = "95%"
|
||||
|
||||
rowLabel = new rowLabel("RenderLoop");
|
||||
|
||||
animationBlock = new animationBlock();
|
||||
|
||||
}
|
||||
188
application/demo/animations/animation.reverse.js
Normal file
188
application/demo/animations/animation.reverse.js
Normal file
@@ -0,0 +1,188 @@
|
||||
|
||||
|
||||
import panelRow from '/elements/panel/row.js';
|
||||
|
||||
import label from '/elements/label.js';
|
||||
|
||||
import button from '/elements/button.js';
|
||||
|
||||
class animationBlock{
|
||||
|
||||
width = 100;
|
||||
|
||||
height = 100;
|
||||
|
||||
margin = 20;
|
||||
|
||||
background = "#03a9f4";
|
||||
|
||||
create() {
|
||||
|
||||
this.animation = this.createAnimation("reverseAnimation");
|
||||
/*
|
||||
var key = this.animation.createKeyFrame( 0 );
|
||||
|
||||
key.setProperty( "rotate", "0deg" );
|
||||
|
||||
|
||||
var key = this.animation.createKeyFrame( 20 );
|
||||
|
||||
key.setProperty( "rotate", "60deg" );
|
||||
|
||||
|
||||
var key = this.animation.createKeyFrame( 60 );
|
||||
|
||||
key.setProperty( "rotate", "120deg" );
|
||||
|
||||
|
||||
var key = this.animation.createKeyFrame( 100 );
|
||||
|
||||
key.setProperty( "rotate", "0deg" );
|
||||
|
||||
*/
|
||||
var key = this.animation.createKeyFrame( 0 );
|
||||
|
||||
key.setProperty( "rotate", "0deg" );
|
||||
|
||||
|
||||
|
||||
var key = this.animation.createKeyFrame( 100 );
|
||||
|
||||
key.setProperty( "rotate", "360deg" );
|
||||
|
||||
this.animation.duration = "2s"
|
||||
|
||||
this.animation.iterationCount = "infinite"
|
||||
|
||||
this.animation.fillMode = "forwards"
|
||||
}
|
||||
|
||||
async mouseover() {
|
||||
|
||||
|
||||
|
||||
|
||||
//this.animation.play();
|
||||
|
||||
}
|
||||
|
||||
async mouseleave() {
|
||||
|
||||
//this.animation.pause();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class forwardButton extends button{
|
||||
|
||||
text = "Direction Forward";
|
||||
|
||||
click() {
|
||||
|
||||
var animation = this.parent.parent.animationBlock.animation;
|
||||
|
||||
animation.direction = "normal"
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class backwardButton extends button{
|
||||
|
||||
text = "Direction Backward";
|
||||
|
||||
click() {
|
||||
|
||||
var animation = this.parent.parent.animationBlock.animation;
|
||||
|
||||
animation.direction = "reverse"
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class pauseButton extends button{
|
||||
|
||||
text = "Pause";
|
||||
|
||||
click() {
|
||||
|
||||
var animation = this.parent.parent.animationBlock.animation;
|
||||
|
||||
|
||||
animation.pause();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class playButton extends button{
|
||||
|
||||
text = "Play";
|
||||
|
||||
click() {
|
||||
|
||||
var animation = this.parent.parent.animationBlock.animation;
|
||||
|
||||
animation.play();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class stopButton extends button{
|
||||
|
||||
text = "Stop";
|
||||
|
||||
click() {
|
||||
|
||||
var animation = this.parent.parent.animationBlock.animation;
|
||||
|
||||
animation.stop();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class rowLabel extends label{
|
||||
|
||||
flex = "1";
|
||||
|
||||
}
|
||||
|
||||
class buttons{
|
||||
|
||||
|
||||
|
||||
playButton = new playButton();
|
||||
|
||||
pauseButton = new pauseButton();
|
||||
|
||||
stopButton = new stopButton();
|
||||
|
||||
forwardButton = new forwardButton();
|
||||
|
||||
backwardButton = new backwardButton();
|
||||
|
||||
|
||||
flexDirection = "column"
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default class row extends panelRow{
|
||||
|
||||
boxWidth = "95%"
|
||||
|
||||
rowLabel = new rowLabel("Reverse");
|
||||
|
||||
buttons = new buttons();
|
||||
|
||||
animationBlock = new animationBlock();
|
||||
|
||||
|
||||
}
|
||||
67
application/demo/animations/animation.rotate.js
Normal file
67
application/demo/animations/animation.rotate.js
Normal file
@@ -0,0 +1,67 @@
|
||||
|
||||
|
||||
import panelRow from '/elements/panel/row.js';
|
||||
|
||||
import label from '/elements/label.js';
|
||||
|
||||
|
||||
class animationBlock{
|
||||
|
||||
width = 100;
|
||||
|
||||
height = 100;
|
||||
|
||||
margin = 20;
|
||||
|
||||
background = "#03a9f4";
|
||||
|
||||
create() {
|
||||
|
||||
this.animation = this.createAnimation("rotateAnimation");
|
||||
|
||||
var key = this.animation.createKeyFrame( 0 );
|
||||
|
||||
key.setProperty( "rotate", "0deg" );
|
||||
|
||||
|
||||
var key = this.animation.createKeyFrame( 20 );
|
||||
|
||||
key.setProperty( "rotate", "60deg" );
|
||||
|
||||
|
||||
var key = this.animation.createKeyFrame( 60 );
|
||||
|
||||
key.setProperty( "rotate", "120deg" );
|
||||
|
||||
|
||||
var key = this.animation.createKeyFrame( 100 );
|
||||
|
||||
key.setProperty( "rotate", "0deg" );
|
||||
|
||||
}
|
||||
|
||||
async click() {
|
||||
|
||||
this.animation.play("2s");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class rowLabel extends label{
|
||||
|
||||
flex = "1";
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default class row extends panelRow{
|
||||
|
||||
boxWidth = "95%"
|
||||
|
||||
rowLabel = new rowLabel("Rotate");
|
||||
|
||||
animationBlock = new animationBlock();
|
||||
|
||||
}
|
||||
145
application/demo/animations/animation.rotateMoveColor.js
Normal file
145
application/demo/animations/animation.rotateMoveColor.js
Normal file
@@ -0,0 +1,145 @@
|
||||
|
||||
|
||||
import panelRow from '/elements/panel/row.js';
|
||||
|
||||
import label from '/elements/label.js';
|
||||
|
||||
|
||||
class animationBlock{
|
||||
|
||||
width = 100;
|
||||
|
||||
height = 100;
|
||||
|
||||
margin = 20;
|
||||
|
||||
background = "#03a9f4";
|
||||
|
||||
|
||||
zIndex = 4;
|
||||
|
||||
|
||||
create() {
|
||||
|
||||
this.rotateAnimation = this.createAnimation("rotateAnimation");
|
||||
|
||||
var key = this.rotateAnimation.createKeyFrame( 0 );
|
||||
|
||||
key.setProperty( "rotate", "0deg" );
|
||||
|
||||
|
||||
var key = this.rotateAnimation.createKeyFrame( 20 );
|
||||
|
||||
key.setProperty( "rotate", "60deg" );
|
||||
|
||||
|
||||
var key = this.rotateAnimation.createKeyFrame( 60 );
|
||||
|
||||
key.setProperty( "rotate", "120deg" );
|
||||
|
||||
|
||||
var key = this.rotateAnimation.createKeyFrame( 100 );
|
||||
|
||||
key.setProperty( "rotate", "0deg" );
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
this.moveAnimation = this.createAnimation("moveAnimation");
|
||||
|
||||
var key = this.moveAnimation.createKeyFrame( 0 );
|
||||
|
||||
key.setProperty( "margin-right", 12 );
|
||||
|
||||
|
||||
var key = this.moveAnimation.createKeyFrame( 20 );
|
||||
|
||||
//key.setProperty( "margin-top", 200 );
|
||||
key.setProperty( "margin-right", 40 );
|
||||
|
||||
|
||||
var key = this.moveAnimation.createKeyFrame( 30 );
|
||||
|
||||
//key.setProperty( "margin-top", 500 );
|
||||
key.setProperty( "margin-right", 120 );
|
||||
|
||||
|
||||
var key = this.moveAnimation.createKeyFrame( 50 );
|
||||
|
||||
key.setProperty( "margin-top", 200 );
|
||||
|
||||
|
||||
var key = this.moveAnimation.createKeyFrame( 100 );
|
||||
|
||||
//key.setProperty( "margin-top", 0 );
|
||||
|
||||
|
||||
|
||||
|
||||
this.backgroundAnimation = this.createAnimation("backgroundAnimation");
|
||||
|
||||
var key = this.backgroundAnimation.createKeyFrame( 0 );
|
||||
|
||||
key.setProperty( "background", "#03a9f4" );
|
||||
|
||||
|
||||
var key = this.backgroundAnimation.createKeyFrame( 40 );
|
||||
|
||||
key.setProperty( "background", "#a6e22e" );
|
||||
|
||||
|
||||
|
||||
var key = this.backgroundAnimation.createKeyFrame( 70 );
|
||||
|
||||
key.setProperty( "background", "#f92672" );
|
||||
|
||||
|
||||
|
||||
var key = this.backgroundAnimation.createKeyFrame( 100 );
|
||||
|
||||
key.setProperty( "background", "#03a9f4" );
|
||||
|
||||
|
||||
}
|
||||
|
||||
async click() {
|
||||
|
||||
|
||||
this.text = "Rotating and moving.";
|
||||
|
||||
this.rotateAnimation.play("2s");
|
||||
|
||||
await this.moveAnimation.play("3s");
|
||||
|
||||
|
||||
|
||||
this.text = "Changing background color.";
|
||||
|
||||
await this.backgroundAnimation.play("2s");
|
||||
|
||||
|
||||
this.text = "Animation is done."
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class rowLabel extends label{
|
||||
|
||||
flex = "1";
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default class row extends panelRow{
|
||||
|
||||
boxWidth = "95%"
|
||||
|
||||
rowLabel = new rowLabel("Rotate + Move + Background");
|
||||
|
||||
animationBlock = new animationBlock();
|
||||
|
||||
}
|
||||
67
application/demo/animations/animation.skewX.js
Normal file
67
application/demo/animations/animation.skewX.js
Normal file
@@ -0,0 +1,67 @@
|
||||
|
||||
|
||||
import panelRow from '/elements/panel/row.js';
|
||||
|
||||
import label from '/elements/label.js';
|
||||
|
||||
|
||||
class animationBlock{
|
||||
|
||||
width = 100;
|
||||
|
||||
height = 100;
|
||||
|
||||
margin = 20;
|
||||
|
||||
background = "#03a9f4";
|
||||
|
||||
create() {
|
||||
|
||||
this.animation = this.createAnimation("rotateAnimation");
|
||||
|
||||
var key = this.animation.createKeyFrame( 0 );
|
||||
|
||||
key.setProperty( "skewX", "0deg" );
|
||||
|
||||
|
||||
var key = this.animation.createKeyFrame( 20 );
|
||||
|
||||
key.setProperty( "skewX", "14deg" );
|
||||
|
||||
|
||||
var key = this.animation.createKeyFrame( 60 );
|
||||
|
||||
key.setProperty( "skewX", "52deg" );
|
||||
|
||||
|
||||
var key = this.animation.createKeyFrame( 100 );
|
||||
|
||||
key.setProperty( "skewX", "0deg" );
|
||||
|
||||
}
|
||||
|
||||
async click() {
|
||||
|
||||
this.animation.play("2s");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class rowLabel extends label{
|
||||
|
||||
flex = "1";
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default class row extends panelRow{
|
||||
|
||||
boxWidth = "95%"
|
||||
|
||||
rowLabel = new rowLabel("skewX");
|
||||
|
||||
animationBlock = new animationBlock();
|
||||
|
||||
}
|
||||
95
application/demo/animations/animations.js
Normal file
95
application/demo/animations/animations.js
Normal file
@@ -0,0 +1,95 @@
|
||||
|
||||
import moveAnimation from "./animation.move.js";
|
||||
|
||||
import rotateAnimation from "./animation.rotate.js";
|
||||
|
||||
import backgroundColor from "./animation.color.js";
|
||||
|
||||
import skewX from "./animation.skewX.js";
|
||||
|
||||
import rotateMoveColor from "./animation.rotateMoveColor.js";
|
||||
|
||||
import reverse from "./animation.reverse.js";
|
||||
|
||||
import render from "./animation.render.js";
|
||||
|
||||
export default class animations{
|
||||
|
||||
overflowY = "auto"
|
||||
|
||||
height = 600;
|
||||
|
||||
width = "auto"
|
||||
|
||||
flexDirection = "column";
|
||||
|
||||
|
||||
scrollbarWidth = "6px";
|
||||
|
||||
scrollbarTrackBackground = "#1c1d1e";
|
||||
|
||||
scrollbarThumbBackground = "#404040"
|
||||
|
||||
scrollbarThumbBorderRadius = "4px"
|
||||
|
||||
scrollbarThumbHoverBackground = "grey";
|
||||
|
||||
|
||||
#ifdef MACOS
|
||||
|
||||
#ifdef DARK
|
||||
|
||||
background = "#282828";
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
background = "#fdfdfd";
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef WINDOWS
|
||||
|
||||
#ifdef DARK
|
||||
|
||||
background = "#202020cc";
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
background = "rgb(255 255 255 / 75%)";
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
layers = 1;
|
||||
|
||||
// height = "-webkit-fill-available";
|
||||
|
||||
padding = 20;
|
||||
|
||||
// width = "100%"
|
||||
|
||||
|
||||
moveAnimation = new moveAnimation();
|
||||
|
||||
rotateAnimation = new rotateAnimation();
|
||||
|
||||
backgroundColor = new backgroundColor();
|
||||
|
||||
skewX = new skewX();
|
||||
|
||||
|
||||
rotateMoveColor = new rotateMoveColor();
|
||||
|
||||
reverse = new reverse();
|
||||
|
||||
render = new render();
|
||||
|
||||
}
|
||||
183
application/demo/application.js
Normal file
183
application/demo/application.js
Normal file
@@ -0,0 +1,183 @@
|
||||
|
||||
|
||||
import minimizeButton from './minimizeButton.js';
|
||||
|
||||
import leftSide from './leftSide/leftSide.js';
|
||||
|
||||
import rightSide from './rightSide/rightSide.js';
|
||||
|
||||
import document from '/unify/document.js';
|
||||
|
||||
import vector2 from '/unify/math/vector2.js';
|
||||
|
||||
import flexbox from '/elements/flexbox.js';
|
||||
|
||||
import frostedGlass from '/elements/window/frostedGlass.js';
|
||||
|
||||
import draggable from '/elements/window/draggable.js';
|
||||
|
||||
|
||||
|
||||
export default class application extends frostedGlass, flexbox, draggable{
|
||||
|
||||
// Children
|
||||
minimizeButton = new minimizeButton();
|
||||
|
||||
leftSide = new leftSide();
|
||||
|
||||
rightSide = new rightSide();
|
||||
|
||||
// Environment
|
||||
mode = "development"
|
||||
|
||||
os = "Windows";
|
||||
|
||||
device = "Pc";
|
||||
|
||||
tint = "Dark";
|
||||
|
||||
|
||||
loadThemes = true;
|
||||
|
||||
|
||||
maxClients = 1000;
|
||||
|
||||
cacheBuildSpeed = 4;
|
||||
|
||||
maxClusters = 1;
|
||||
|
||||
//serverAddress = "192.168.178.15";
|
||||
|
||||
|
||||
// Styling
|
||||
position = "absolute";
|
||||
|
||||
borderRadius = 12;
|
||||
|
||||
boxBackgroundSize = "1000px 1000px";
|
||||
|
||||
boxTransition = "background-image 0.1s ease-in-out";
|
||||
|
||||
boxHeight = "100vh";
|
||||
|
||||
position = "absolute";
|
||||
|
||||
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
flexDirection = "column";
|
||||
|
||||
#else
|
||||
|
||||
flexDirection = "row";
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
// Pragma's
|
||||
#ifdef ANDROID
|
||||
|
||||
lastPosition = new vector2( 0, 0 );
|
||||
|
||||
fontFamily = "android";
|
||||
|
||||
transform = "translate(0px, 0px)";
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef WINDOWS
|
||||
|
||||
fontFamily = "SegoeUI";
|
||||
|
||||
#ifdef DARK
|
||||
|
||||
//boxBackgroundImage = "url('/assets/images/wallpapers/windows/light/2048.png')";
|
||||
|
||||
//backgroundImage = "url('/assets/images/wallpapers/windows/light/blur_1024.jpg')";
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
//boxBackgroundImage = "url('/assets/images/wallpapers/windows/light/2048.jpg')";
|
||||
|
||||
//backgroundImage = "url('/assets/images/wallpapers/windows/light/blur_1024.jpg')";
|
||||
|
||||
#endif
|
||||
|
||||
border = "1px solid rgb(65 84 118 / 32%)";
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef MACOS
|
||||
|
||||
fontFamily = "Inter";
|
||||
|
||||
#ifdef DARK
|
||||
|
||||
//boxBackgroundImage = "url('/assets/images/wallpapers/ventura/dark/dark.jpg')";
|
||||
|
||||
//backgroundImage = "url('/assets/images/wallpapers/ventura/dark/darkBlur.jpg')";
|
||||
|
||||
border = "1px solid #8f8f8f59";
|
||||
|
||||
outline = "1px solid black";
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
//boxBackgroundImage = "url('/assets/images/wallpapers/ventura/light/light.jpg')";
|
||||
|
||||
//backgroundImage = "url('/assets/images/wallpapers/ventura/light/lightBlur.jpg')";
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// Methods
|
||||
afterLoad() {
|
||||
|
||||
console.log( "loaded application", this );
|
||||
|
||||
this.centerObject();
|
||||
|
||||
}
|
||||
|
||||
centerObject() {
|
||||
|
||||
var domWindow = document.defaultView;
|
||||
|
||||
this.windowHeight = domWindow.innerHeight;
|
||||
|
||||
this.windowWidth = domWindow.innerWidth;
|
||||
|
||||
|
||||
var boundBox = this.defaultElement.getBoundingClientRect();
|
||||
|
||||
|
||||
var width = boundBox.width;
|
||||
|
||||
var height = boundBox.height;
|
||||
|
||||
|
||||
var x = this.windowWidth / 2 - ( width / 2 );
|
||||
|
||||
var y = this.windowHeight / 2 - ( height / 2 );
|
||||
|
||||
|
||||
this.lastPosition = new vector2( Math.round( x ), Math.round( y ) );
|
||||
|
||||
}
|
||||
|
||||
click() {
|
||||
|
||||
this.boxShadow = "1px 1px 3px 0px #00000054"
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
11
application/demo/comment/comment.body.js
Normal file
11
application/demo/comment/comment.body.js
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
|
||||
import column from '/unify/column.js';
|
||||
|
||||
|
||||
export default class body extends column {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
22
application/demo/comment/comment.js
Normal file
22
application/demo/comment/comment.js
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
import table from '/unify/table.js';
|
||||
|
||||
import user from '/user/user.js';
|
||||
|
||||
import body from './comment.body.js';
|
||||
|
||||
import title from './comment.title.js';
|
||||
|
||||
|
||||
export default class comment extends table{
|
||||
|
||||
author = new user();
|
||||
|
||||
title = new title();
|
||||
|
||||
body = new body();
|
||||
|
||||
flexDirection = "column";
|
||||
|
||||
}
|
||||
|
||||
40
application/demo/comment/comment.title.js
Normal file
40
application/demo/comment/comment.title.js
Normal file
@@ -0,0 +1,40 @@
|
||||
|
||||
|
||||
import column from '/unify/column.js';
|
||||
|
||||
|
||||
export default class title extends column {
|
||||
|
||||
padding = 20;
|
||||
|
||||
color = "black";
|
||||
|
||||
label = "title";
|
||||
|
||||
useCustomElement = true;
|
||||
|
||||
|
||||
async keyup( event ){
|
||||
|
||||
this.value = event.target.value;
|
||||
|
||||
this.animate(150, 400, function( value ){
|
||||
|
||||
this.height = value;
|
||||
|
||||
})
|
||||
|
||||
var result = await this.socketManager.get( "column", "update", this, "keyup" );
|
||||
|
||||
}
|
||||
|
||||
serverKeyup( object ) {
|
||||
|
||||
this.value = object.value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
48
application/demo/comment/comments.messages.js
Normal file
48
application/demo/comment/comments.messages.js
Normal file
@@ -0,0 +1,48 @@
|
||||
|
||||
import renderCollection from '/unify/renderCollection.js';
|
||||
|
||||
import groups from '/user/group/user.group.permission.js';
|
||||
|
||||
import OR from '/unify/sql/OR.js';
|
||||
|
||||
import AND from '/unify/sql/AND.js';
|
||||
|
||||
import LIKE from '/unify/sql/LIKE.js';
|
||||
|
||||
|
||||
export default class commentsMessages extends renderCollection {
|
||||
|
||||
flexFlow = "column";
|
||||
|
||||
direction = "desc";
|
||||
|
||||
width = "-webkit-fill-available";
|
||||
|
||||
marginTop = 20;
|
||||
|
||||
debug = true;
|
||||
|
||||
node async search( value ) {
|
||||
|
||||
var filter = this.getFilter();
|
||||
|
||||
filter.search = OR( LIKE( filter.body, value ), LIKE( filter.title, value ) );
|
||||
|
||||
filter.direction = "desc";
|
||||
|
||||
}
|
||||
|
||||
permission() {
|
||||
|
||||
this.allow( groups.visitor, "READ" );
|
||||
|
||||
this.allow( groups.member, "READ" );
|
||||
|
||||
this.allow( groups.admin, "READ" );
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
8
application/demo/comment/create/comment.create.author.js
Normal file
8
application/demo/comment/create/comment.create.author.js
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
import user from '/user/user.js';
|
||||
|
||||
export default class commentEditAuthor extends user{
|
||||
|
||||
display = "none";
|
||||
|
||||
}
|
||||
16
application/demo/comment/create/comment.create.body.js
Normal file
16
application/demo/comment/create/comment.create.body.js
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
|
||||
import commentBody from '../comment.body.js';
|
||||
|
||||
import textarea from '/elements/textarea.js';
|
||||
|
||||
|
||||
export default class commentEditBody extends commentBody, textarea{
|
||||
|
||||
useCustomElement = true;
|
||||
|
||||
height = "97px";
|
||||
|
||||
placeholder = "Message";
|
||||
|
||||
}
|
||||
103
application/demo/comment/create/comment.create.js
Normal file
103
application/demo/comment/create/comment.create.js
Normal file
@@ -0,0 +1,103 @@
|
||||
|
||||
import comment from '../comment.js';
|
||||
|
||||
import saveButton from './comment.saveButton.js';
|
||||
|
||||
import userLabel from './comment.userLabel.js';
|
||||
|
||||
import commentEditTitle from './comment.create.title.js';
|
||||
|
||||
import commentEditBody from './comment.create.body.js';
|
||||
|
||||
import commentEditAuthor from './comment.create.author.js';
|
||||
|
||||
import header from '/elements/header.js';
|
||||
|
||||
import collection from '/unify/collection.js';
|
||||
|
||||
import groups from '/user/group/user.group.permission.js';
|
||||
|
||||
|
||||
|
||||
export default class createComment extends comment{
|
||||
|
||||
display = "flex";
|
||||
|
||||
body = new commentEditBody();
|
||||
|
||||
saveButton = new saveButton();
|
||||
|
||||
title = false;
|
||||
|
||||
author = this.user; // bug destroys the permission system
|
||||
|
||||
|
||||
#ifdef WINDOWS
|
||||
|
||||
#ifdef DARK
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef MACOS
|
||||
|
||||
#ifdef DARK
|
||||
|
||||
background = "#00000042";
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
background = "#ffffffd1";
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
width = "50vw";
|
||||
|
||||
debug = true;
|
||||
|
||||
width = "100%";
|
||||
|
||||
marginTop = 40;
|
||||
|
||||
|
||||
async create() {
|
||||
|
||||
this.body.value = "";
|
||||
|
||||
this.setID( false );
|
||||
|
||||
}
|
||||
|
||||
disableWRITE() {
|
||||
|
||||
this.hide();
|
||||
|
||||
}
|
||||
|
||||
enableWRITE() {
|
||||
|
||||
this.show();
|
||||
|
||||
}
|
||||
|
||||
permission() {
|
||||
|
||||
this.allow( groups.member, "WRITE" );
|
||||
|
||||
this.allow( groups.admin, "WRITE" );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
8
application/demo/comment/create/comment.create.title.js
Normal file
8
application/demo/comment/create/comment.create.title.js
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
|
||||
import commentTitle from '../comment.title.js';
|
||||
|
||||
|
||||
export default class commentEditTitle extends commentTitle{
|
||||
|
||||
}
|
||||
29
application/demo/comment/create/comment.saveButton.js
Normal file
29
application/demo/comment/create/comment.saveButton.js
Normal file
@@ -0,0 +1,29 @@
|
||||
|
||||
import button from '/elements/button.js';
|
||||
|
||||
import tools from '/unify/tools.js';
|
||||
|
||||
|
||||
export default class saveCommentButton extends button {
|
||||
|
||||
label = "Create comment";
|
||||
|
||||
async click( event ){
|
||||
|
||||
var result = await this.socketManager.get( "table", "save", this.parent );
|
||||
|
||||
this.parent.create();
|
||||
|
||||
await this.parent.parent.commentsMessages.sync();
|
||||
|
||||
this.parent.parent.customElement.scrollTo( 0, this.parent.parent.customElement.scrollHeight);
|
||||
|
||||
console.log("laatste", this.parent.parent.customElement.scrollHeight);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
38
application/demo/comment/create/comment.userLabel.js
Normal file
38
application/demo/comment/create/comment.userLabel.js
Normal file
@@ -0,0 +1,38 @@
|
||||
|
||||
import input from '/elements/input.js';
|
||||
|
||||
export default class userLabel extends input{
|
||||
|
||||
float = "right";
|
||||
|
||||
useCustomElement = false;
|
||||
|
||||
height = 20;
|
||||
|
||||
float = "right";
|
||||
|
||||
marginLeft = 100;
|
||||
|
||||
marginTop = 20;
|
||||
|
||||
|
||||
setAuthor( author ) {
|
||||
|
||||
if( author.username ) {
|
||||
|
||||
this.value = "author: " + author.username.value;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
create() {
|
||||
|
||||
var author = this.parent.parent.author;
|
||||
|
||||
this.setAuthor( author );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
37
application/demo/comment/edit/comment.deleteButton.js
Normal file
37
application/demo/comment/edit/comment.deleteButton.js
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
import button from '/elements/button.js';
|
||||
|
||||
export default class deleteButton extends button {
|
||||
|
||||
label = "Delete";
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
fontSize = 12;
|
||||
|
||||
width = "auto"
|
||||
|
||||
height = "auto"
|
||||
|
||||
#endif
|
||||
|
||||
async click() {
|
||||
|
||||
var sure = confirm("Are you sure you want to delete this Post");
|
||||
|
||||
if( sure ) {
|
||||
|
||||
this.parent.parent.delete();
|
||||
|
||||
this.parent.parent.remove();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
10
application/demo/comment/edit/comment.edit.author.js
Normal file
10
application/demo/comment/edit/comment.edit.author.js
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
|
||||
import user from '/user/user.js';
|
||||
|
||||
|
||||
export default class commentEditAuthor extends user{
|
||||
|
||||
display = "none";
|
||||
|
||||
}
|
||||
109
application/demo/comment/edit/comment.edit.body.js
Normal file
109
application/demo/comment/edit/comment.edit.body.js
Normal file
@@ -0,0 +1,109 @@
|
||||
|
||||
import commentBody from '../comment.body.js';
|
||||
|
||||
import textarea from '/elements/textarea.js';
|
||||
|
||||
import document from '/unify/document.js';
|
||||
|
||||
import flexbox from '/elements/flexbox.js';
|
||||
|
||||
|
||||
export default class commentEditBody extends commentBody, flexbox{
|
||||
|
||||
customElement = document.createElement("textarea");
|
||||
|
||||
useCustomElement = true;
|
||||
|
||||
width = "-webkit-fill-available"
|
||||
|
||||
padding = 20;
|
||||
|
||||
|
||||
#ifdef MACOS
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
background = "white";
|
||||
|
||||
color = "black";
|
||||
|
||||
borderRadius = 12;
|
||||
|
||||
margin = 6;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef DARK
|
||||
|
||||
background = "#282828!important";
|
||||
|
||||
|
||||
//color = "white";
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef WINDOWS
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
background = "none";
|
||||
|
||||
color = "black";
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef DARK
|
||||
|
||||
background = "#202020cc";
|
||||
|
||||
borderRadius = 12;
|
||||
|
||||
#endif
|
||||
|
||||
margin = 16;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
async keyup( event ){
|
||||
|
||||
this.value = event.target.value;
|
||||
|
||||
var result = await this.socketManager.get( "column", "update", this, "keyup" );
|
||||
|
||||
}
|
||||
|
||||
|
||||
create() {
|
||||
|
||||
this.deactivateTextarea()
|
||||
|
||||
}
|
||||
|
||||
activateTextarea() {
|
||||
|
||||
this.useCustomElement = true;
|
||||
|
||||
}
|
||||
|
||||
deactivateTextarea() {
|
||||
|
||||
this.useCustomElement = false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
useCustomElement = false;
|
||||
|
||||
fontSize = 14;
|
||||
|
||||
//color = "red";
|
||||
|
||||
}
|
||||
127
application/demo/comment/edit/comment.edit.js
Normal file
127
application/demo/comment/edit/comment.edit.js
Normal file
@@ -0,0 +1,127 @@
|
||||
|
||||
import comment from '../comment.js';
|
||||
|
||||
import saveButton from './comment.saveButton.js';
|
||||
|
||||
import commentEditTitle from './comment.edit.title.js';
|
||||
|
||||
import commentEditBody from './comment.edit.body.js';
|
||||
|
||||
import commentEditAuthor from './comment.edit.author.js';
|
||||
|
||||
import header from '/elements/header.js';
|
||||
|
||||
import collection from '/unify/collection.js';
|
||||
|
||||
import userLabel from './comment.userLabel.js';
|
||||
|
||||
import deleteButton from './comment.deleteButton.js';
|
||||
|
||||
import editButton from './comment.editButton.js';
|
||||
|
||||
import information from './comment.information.js';
|
||||
|
||||
|
||||
|
||||
export default class editComment extends comment{
|
||||
|
||||
|
||||
|
||||
|
||||
layers = 1;
|
||||
|
||||
display = "flex";
|
||||
|
||||
debug = true;
|
||||
|
||||
flexFlow = "column";
|
||||
|
||||
gridTemplate = " '_information ' " +
|
||||
" 'body ' " +
|
||||
" 'body ' " +
|
||||
" 'saveButton ' ";
|
||||
|
||||
|
||||
_information = new information();
|
||||
|
||||
body = new commentEditBody();
|
||||
|
||||
title = new commentEditTitle();
|
||||
|
||||
saveButton = new saveButton();
|
||||
|
||||
|
||||
|
||||
|
||||
width = "-webkit-fill-available";
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
width = "100vw"
|
||||
|
||||
borderRadius = 18
|
||||
|
||||
margin = 4
|
||||
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
background = "white";
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
create() {
|
||||
|
||||
this.title.hide();
|
||||
|
||||
this.author.disable = true;
|
||||
|
||||
if( !this.id ) {
|
||||
|
||||
this.body.useCustomElement = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enableWRITE() {
|
||||
|
||||
this._information._editButton.show();
|
||||
|
||||
}
|
||||
|
||||
disableWRITE() {
|
||||
|
||||
this._information._editButton.hide();
|
||||
|
||||
this.body.useCustomElement = false;
|
||||
|
||||
this.saveButton.hide();
|
||||
|
||||
}
|
||||
|
||||
enableDELETE() {
|
||||
|
||||
this._information._deleteButton.show();
|
||||
|
||||
}
|
||||
|
||||
disableDELETE() {
|
||||
|
||||
this._information._deleteButton.hide();
|
||||
|
||||
}
|
||||
|
||||
permission() {
|
||||
|
||||
this.allow( this.author, "WRITE" );
|
||||
this.allow( this.author, "DELETE" );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
31
application/demo/comment/edit/comment.edit.title.js
Normal file
31
application/demo/comment/edit/comment.edit.title.js
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
|
||||
import commentTitle from '../comment.title.js';
|
||||
|
||||
|
||||
export default class commentEditTitle extends commentTitle{
|
||||
|
||||
|
||||
useCustomElement = false;
|
||||
|
||||
borderLeft = "solid 1px #faebd7";
|
||||
|
||||
borderRight = "solid 1px #faebd7";
|
||||
|
||||
enableInput() {
|
||||
|
||||
this.background = "#373b44";
|
||||
|
||||
this.useCustomElement = true;
|
||||
|
||||
}
|
||||
|
||||
disableInput() {
|
||||
|
||||
this.background = "white";
|
||||
|
||||
this.useCustomElement = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
35
application/demo/comment/edit/comment.editButton.js
Normal file
35
application/demo/comment/edit/comment.editButton.js
Normal file
@@ -0,0 +1,35 @@
|
||||
|
||||
import button from '/elements/button.js';
|
||||
|
||||
export default class editButton extends button{
|
||||
|
||||
label = "Edit";
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
fontSize = 12;
|
||||
|
||||
width = "auto"
|
||||
|
||||
height = "auto"
|
||||
|
||||
#endif
|
||||
|
||||
async click() {
|
||||
|
||||
this.parent.parent.body.activateTextarea();
|
||||
|
||||
this.parent.parent.saveButton.show();
|
||||
|
||||
this.hide();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
70
application/demo/comment/edit/comment.information.js
Normal file
70
application/demo/comment/edit/comment.information.js
Normal file
@@ -0,0 +1,70 @@
|
||||
|
||||
import userLabel from './comment.userLabel.js';
|
||||
|
||||
import deleteButton from './comment.deleteButton.js';
|
||||
|
||||
import editButton from './comment.editButton.js';
|
||||
|
||||
import icon from '/elements/icon.js';
|
||||
|
||||
|
||||
class chatIcon extends icon{
|
||||
|
||||
margin = 12;
|
||||
|
||||
|
||||
}
|
||||
|
||||
export default class information{
|
||||
|
||||
|
||||
width = "100%";
|
||||
|
||||
display = "grid";
|
||||
|
||||
display = "flex";
|
||||
|
||||
flexFlow = "row";
|
||||
|
||||
layers = 2;
|
||||
|
||||
borderBottom = "#2b2c2d57";
|
||||
|
||||
borderTop = "#2b2c2d57";
|
||||
|
||||
|
||||
gridTemplate = " '_deleteButton _editButton' " +
|
||||
" 'userLabel userLabel' ";
|
||||
|
||||
gridTemplateColumns = "40px 100px";
|
||||
|
||||
gridTemplateRows = "40px 60px";
|
||||
|
||||
#ifdef WINDOWS
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
background = "#4b94d31f";
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef DARK
|
||||
|
||||
//background = "rgb(48 51 56 / 86%)";
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
_deleteButton = new deleteButton( );
|
||||
|
||||
_editButton = new editButton( );
|
||||
|
||||
_userLabel = new userLabel();
|
||||
|
||||
//_icon = new chatIcon("ios-chatbubbles-outline.svg", true);
|
||||
|
||||
}
|
||||
36
application/demo/comment/edit/comment.saveButton.js
Normal file
36
application/demo/comment/edit/comment.saveButton.js
Normal file
@@ -0,0 +1,36 @@
|
||||
|
||||
import button from '/elements/button.js';
|
||||
|
||||
import tools from '/unify/tools.js';
|
||||
|
||||
|
||||
export default class saveEditButton extends button {
|
||||
|
||||
text = "Save Message";
|
||||
|
||||
display = "none";
|
||||
|
||||
userContract;
|
||||
|
||||
|
||||
async click( event ){
|
||||
|
||||
var result = await this.socketManager.get( "table", "save", this, "sign" );
|
||||
|
||||
this.parent.id = false;
|
||||
|
||||
this.hide();
|
||||
|
||||
this.parent._information._editButton.show();
|
||||
|
||||
this.parent.body.useCustomElement = false;
|
||||
|
||||
//this.parent.background = "#cdf0ce";
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
34
application/demo/comment/edit/comment.userLabel.js
Normal file
34
application/demo/comment/edit/comment.userLabel.js
Normal file
@@ -0,0 +1,34 @@
|
||||
|
||||
import input from '/elements/input.js';
|
||||
|
||||
import label from '/elements/label.js';
|
||||
|
||||
export default class userLabel extends label {
|
||||
|
||||
float = "left";
|
||||
|
||||
fontWeight = "bold";
|
||||
|
||||
padding = "12px";
|
||||
|
||||
paddingLeft = 26;
|
||||
|
||||
setAuthor( author ) {
|
||||
|
||||
if( author.username ) {
|
||||
|
||||
this.text = author.username.value;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
create() {
|
||||
|
||||
var author = this.parent.parent.author;
|
||||
|
||||
this.setAuthor( author );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
19
application/demo/edit/news.edit.body.js
Normal file
19
application/demo/edit/news.edit.body.js
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
import newsBody from '../news.body.js';
|
||||
|
||||
import textarea from '/elements/textarea.js';
|
||||
|
||||
|
||||
export default class newsPageBody extends newsBody, textarea{
|
||||
|
||||
placeholder = "Message";
|
||||
|
||||
height = 120;
|
||||
|
||||
async keyup( event ) {
|
||||
|
||||
this.value = event.target.value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
39
application/demo/edit/news.edit.button.js
Normal file
39
application/demo/edit/news.edit.button.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import button from '/elements/button.js';
|
||||
|
||||
export default class editButton extends button {
|
||||
|
||||
label = "Save";
|
||||
|
||||
async click( event, object ){
|
||||
|
||||
var result = await this.parent.parent.save();
|
||||
|
||||
// reset id so you can create a new row again
|
||||
var editNewsDialog = this.parent.parent;
|
||||
|
||||
editNewsDialog.id = false;
|
||||
|
||||
// reset title
|
||||
editNewsDialog.newsTitleRow.title.value = "";
|
||||
|
||||
// reset body
|
||||
editNewsDialog.newsBodyRow.body.value = "";
|
||||
|
||||
editNewsDialog.hide();
|
||||
|
||||
this.parents("newsPages").newsPage.sync();
|
||||
|
||||
if( this.parents("newsItemPage").newsListTable ) {
|
||||
|
||||
this.parents("newsItemPage").newsListTable.body.update();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
246
application/demo/edit/news.edit.js
Normal file
246
application/demo/edit/news.edit.js
Normal file
@@ -0,0 +1,246 @@
|
||||
|
||||
|
||||
import news from '../news.js';
|
||||
|
||||
import newsEditTitle from './news.edit.title.js';
|
||||
|
||||
import newsEditBody from './news.edit.body.js';
|
||||
|
||||
import newsEditbutton from './news.edit.button.js';
|
||||
|
||||
import newsEditPrice from './news.edit.price.js';
|
||||
|
||||
|
||||
import groups from '/user/group/user.group.permission.js';
|
||||
|
||||
import label from '/elements/label/left.js';
|
||||
|
||||
import panel from '/elements/panel.js';
|
||||
|
||||
import frostedGlass from '/elements/window/frostedGlass.js';
|
||||
|
||||
import draggable from '/elements/window/draggable.js';
|
||||
|
||||
import button from '/elements/button.js';
|
||||
|
||||
|
||||
import panelRow from '/elements/panel/row.js';
|
||||
|
||||
class newsBodyRow extends panelRow{
|
||||
|
||||
#ifdef WINDOWS
|
||||
|
||||
background = "none";
|
||||
|
||||
#endif
|
||||
|
||||
border = "none"
|
||||
|
||||
label = new label("Message");
|
||||
|
||||
body = new newsEditBody();
|
||||
|
||||
}
|
||||
|
||||
class newsTitleRow extends panelRow{
|
||||
|
||||
#ifdef WINDOWS
|
||||
|
||||
background = "none";
|
||||
|
||||
#endif
|
||||
|
||||
border = "none"
|
||||
|
||||
label = new label("Title");
|
||||
|
||||
title = new newsEditTitle();
|
||||
|
||||
}
|
||||
|
||||
class newsPriceRow extends panelRow{
|
||||
|
||||
#ifdef WINDOWS
|
||||
|
||||
background = "none";
|
||||
|
||||
#endif
|
||||
|
||||
border = "none"
|
||||
|
||||
label = new label("Price");
|
||||
|
||||
price = new newsEditPrice();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
class cancelButton extends button{
|
||||
|
||||
text = "Cancel";
|
||||
|
||||
boxWidth = "100%"
|
||||
|
||||
|
||||
click() {
|
||||
|
||||
this.parent.parent.hide();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class newsButtonRow extends panelRow{
|
||||
|
||||
border = "none"
|
||||
|
||||
cancelButton = new cancelButton();
|
||||
|
||||
newsEditbutton = new newsEditbutton();
|
||||
|
||||
|
||||
#ifdef WINDOWS
|
||||
|
||||
background = "none";
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
import header from '/elements/window/header.js';
|
||||
|
||||
export default class newsEdit extends news, panel, draggable {
|
||||
|
||||
header = new header("News");
|
||||
|
||||
layers = 2;
|
||||
|
||||
zIndex = 10000;
|
||||
|
||||
|
||||
#ifdef WINDOWS
|
||||
|
||||
fontFamily = "segoe";
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef MACOS
|
||||
|
||||
fontFamily = "sf-ui";
|
||||
|
||||
width = 600;
|
||||
|
||||
#ifdef DARK
|
||||
|
||||
background = "#161110bf";
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
//background = "white";
|
||||
|
||||
background = "#fdfdfdbf"
|
||||
|
||||
#endif
|
||||
|
||||
backdropFilter = "blur(22px)";
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef WINDOWS
|
||||
|
||||
fontFamily = "SegoeUI";
|
||||
|
||||
width = 600;
|
||||
|
||||
#ifdef DARK
|
||||
|
||||
background = "#202020cc";
|
||||
|
||||
border = "1px solid rgb(44 45 46)";
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
//background = "white";
|
||||
|
||||
background = "#fdfdfdbf"
|
||||
|
||||
#endif
|
||||
|
||||
backdropFilter = "blur(22px)";
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
selector = "#application";
|
||||
|
||||
display = "none";
|
||||
|
||||
flexDirection = "column";
|
||||
|
||||
debug = true;
|
||||
|
||||
position = "absolute"
|
||||
|
||||
boxBackgroundImage;
|
||||
|
||||
newsTitleRow = new newsTitleRow();
|
||||
|
||||
newsPriceRow = new newsPriceRow();
|
||||
|
||||
newsBodyRow = new newsBodyRow();
|
||||
|
||||
newsButtonRow = new newsButtonRow();
|
||||
|
||||
|
||||
|
||||
debug = true;
|
||||
|
||||
height = "fit-content";
|
||||
|
||||
async create() {
|
||||
|
||||
//await this.sync();
|
||||
|
||||
//this.hide();
|
||||
|
||||
}
|
||||
|
||||
afterLoad() {
|
||||
|
||||
this.center();
|
||||
|
||||
}
|
||||
|
||||
permission() {
|
||||
|
||||
this.allow( groups.member, "READ" );
|
||||
|
||||
this.allow( groups.admin, "READ" );
|
||||
|
||||
this.allow( groups.visitor, "READ" );
|
||||
|
||||
this.allow( groups.member, "WRITE" );
|
||||
|
||||
this.allow( groups.admin, "WRITE" );
|
||||
|
||||
this.allow( groups.visitor, "WRITE" );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
18
application/demo/edit/news.edit.price.js
Normal file
18
application/demo/edit/news.edit.price.js
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
import newsPrice from '../news.price.js';
|
||||
|
||||
import input from '/elements/input.js';
|
||||
|
||||
|
||||
export default class newsPagePrice extends newsPrice, input{
|
||||
|
||||
placeholder = "Price";
|
||||
|
||||
|
||||
async keyup( event ) {
|
||||
|
||||
this.value = event.target.value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
11
application/demo/edit/news.edit.title.js
Normal file
11
application/demo/edit/news.edit.title.js
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
import newsTitle from '../news.title.js';
|
||||
|
||||
import input from '/elements/input.js';
|
||||
|
||||
|
||||
export default class newsEditTitle extends input, newsTitle{
|
||||
|
||||
placeholder = "Title";
|
||||
|
||||
}
|
||||
140
application/demo/examples/grids.js
Normal file
140
application/demo/examples/grids.js
Normal file
@@ -0,0 +1,140 @@
|
||||
|
||||
import input from "/elements/input.js"
|
||||
|
||||
import page from "/elements/page.js"
|
||||
|
||||
|
||||
class label{
|
||||
|
||||
constructor( text ) {
|
||||
|
||||
this.text = text;
|
||||
|
||||
}
|
||||
|
||||
background = "#0000002e"
|
||||
|
||||
borderRadius = 6;
|
||||
|
||||
margin = 10;
|
||||
|
||||
padding = 26;
|
||||
|
||||
}
|
||||
|
||||
class a extends input{
|
||||
|
||||
boxAlignItems = "center"
|
||||
|
||||
boxJustifyContent = "center";
|
||||
|
||||
boxColor = "black"
|
||||
|
||||
}
|
||||
|
||||
|
||||
class b extends input{
|
||||
|
||||
//background = "blue";
|
||||
|
||||
boxAlignItems = "center"
|
||||
|
||||
boxJustifyContent = "center";
|
||||
|
||||
boxColor = "black"
|
||||
|
||||
}
|
||||
|
||||
class c extends input{
|
||||
|
||||
//background = "yellow";
|
||||
|
||||
boxAlignItems = "center"
|
||||
|
||||
boxJustifyContent = "center";
|
||||
|
||||
boxColor = "black"
|
||||
|
||||
|
||||
}
|
||||
|
||||
class d extends input{
|
||||
|
||||
//boxBackground = "grey";
|
||||
|
||||
boxAlignItems = "center"
|
||||
|
||||
boxJustifyContent = "center";
|
||||
|
||||
boxColor = "black"
|
||||
|
||||
|
||||
}
|
||||
|
||||
class gridA{
|
||||
|
||||
display = "grid";
|
||||
|
||||
gridTemplate = `
|
||||
|
||||
"label label"
|
||||
"a a"
|
||||
"b d"
|
||||
"c d"
|
||||
`;
|
||||
|
||||
height = 400;
|
||||
|
||||
width = "100%"
|
||||
|
||||
label = new label("This is the first Grid, Press tab to navigate trough the inputs.");
|
||||
|
||||
a = new a();
|
||||
|
||||
b = new b();
|
||||
|
||||
c = new c();
|
||||
|
||||
d = new d();
|
||||
|
||||
}
|
||||
|
||||
|
||||
class gridB{
|
||||
|
||||
display = "grid";
|
||||
|
||||
gridTemplate = `
|
||||
|
||||
"label label"
|
||||
"d d"
|
||||
"a empty"
|
||||
"b b"
|
||||
`;
|
||||
|
||||
height = 400;
|
||||
|
||||
width = "100%"
|
||||
|
||||
label = new label("This is the second Grid, Press tab to navigate trough the inputs.");
|
||||
|
||||
a = new a();
|
||||
|
||||
b = new b();
|
||||
|
||||
c = new c();
|
||||
|
||||
d = new d();
|
||||
|
||||
}
|
||||
|
||||
export default class gridExample extends page{
|
||||
|
||||
flexDirection = "column"
|
||||
|
||||
|
||||
gridA = new gridA();
|
||||
|
||||
gridB = new gridB();
|
||||
|
||||
}
|
||||
102
application/demo/fileManager/fileManager.icon.deleteButton.js
Normal file
102
application/demo/fileManager/fileManager.icon.deleteButton.js
Normal file
@@ -0,0 +1,102 @@
|
||||
|
||||
import icon from "/elements/icon.js";
|
||||
|
||||
|
||||
|
||||
#ifdef SERVER
|
||||
|
||||
import fs from "fs";
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
const delay = time => new Promise(res=>setTimeout(res,time));
|
||||
|
||||
|
||||
export default class deleteFileIconButton extends icon{
|
||||
|
||||
width = 24;
|
||||
|
||||
height = 24;
|
||||
|
||||
propegateEvent = false;
|
||||
|
||||
boxMarginTop = "-12px";
|
||||
|
||||
boxBorderRadius = 14;
|
||||
|
||||
boxBackground = "#ffffffbf";
|
||||
|
||||
boxWidth = "fit-content";
|
||||
|
||||
boxPadding = 2;
|
||||
|
||||
boxPosition = "absolute";
|
||||
|
||||
boxMarginLeft = -8;
|
||||
|
||||
boxDisplay = "none";
|
||||
|
||||
|
||||
async click() {
|
||||
|
||||
this.parent.opacity = "0%";
|
||||
|
||||
await delay(200)
|
||||
|
||||
this.parent.background = "none";
|
||||
|
||||
this.parent.width = 0;
|
||||
|
||||
this.parent.margin = 0;
|
||||
|
||||
this.parent.padding = 0;
|
||||
|
||||
this.parent.border = "none"
|
||||
|
||||
await delay(200)
|
||||
|
||||
this.parent.hide();
|
||||
|
||||
this.parent.remove();
|
||||
|
||||
var fileName = this.parent.value;
|
||||
|
||||
await this.removeFile( fileName );
|
||||
|
||||
}
|
||||
|
||||
node async removeFile( fileName ) {
|
||||
|
||||
var absolutePath = path.resolve( "./assets/uploads/" + fileName );
|
||||
|
||||
console.log("Removing file test", absolutePath);
|
||||
|
||||
if( fs.existsSync( absolutePath ) ) {
|
||||
|
||||
fs.unlinkSync( absolutePath );
|
||||
|
||||
console.log("File is removed.");
|
||||
|
||||
} else {
|
||||
|
||||
console.log("File does not exist.");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
constructor() {
|
||||
|
||||
super("close.svg")
|
||||
|
||||
}
|
||||
|
||||
create() {
|
||||
|
||||
this.hide();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
164
application/demo/fileManager/fileManager.icon.js
Normal file
164
application/demo/fileManager/fileManager.icon.js
Normal file
@@ -0,0 +1,164 @@
|
||||
|
||||
import icon from "/elements/icon.js";
|
||||
|
||||
import deleteButton from "./fileManager.icon.deleteButton.js";
|
||||
|
||||
|
||||
export default class fileIcon extends icon{
|
||||
|
||||
boxSizing = "border-box"
|
||||
|
||||
border = "none"
|
||||
|
||||
opacity = "100%"
|
||||
|
||||
fontSize = "0"
|
||||
|
||||
propegateEvent = false;
|
||||
|
||||
backgroundSize = "cover!important"
|
||||
|
||||
width = 60;
|
||||
|
||||
height = 60;
|
||||
|
||||
borderRadius = 12;
|
||||
|
||||
margin = 6;
|
||||
|
||||
display = "block";
|
||||
|
||||
float = "left";
|
||||
|
||||
layers = 1;
|
||||
|
||||
border = "2px solid #F7FAFC"
|
||||
|
||||
cursor = "pointer";
|
||||
|
||||
|
||||
deleteButton = new deleteButton();
|
||||
|
||||
mode = "show";
|
||||
|
||||
//transition = "2s"
|
||||
|
||||
toggleEditMode() {
|
||||
|
||||
if( this.mode == "show" ) {
|
||||
|
||||
this.deleteButton.show();
|
||||
|
||||
this.mode = "edit";
|
||||
|
||||
this.rotateAnimation.play();
|
||||
|
||||
} else {
|
||||
|
||||
this.deleteButton.hide();
|
||||
|
||||
this.mode = "show";
|
||||
|
||||
this.rotateAnimation.stop();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
create() {
|
||||
|
||||
this.setImage( "'/assets/uploads/" + this.value + "'" );
|
||||
|
||||
this.createKeyFrame();
|
||||
|
||||
this.opacityAnimation.play();
|
||||
|
||||
}
|
||||
|
||||
createKeyFrame() {
|
||||
|
||||
this.rotateAnimation = this.createAnimation("rotateAnimation");
|
||||
|
||||
var randomTime = "0."+ 2 + Math.floor(Math.random() *1000);
|
||||
|
||||
this.rotateAnimation.setDuration( randomTime +"s" );
|
||||
|
||||
this.rotateAnimation.setIterationCount("infinite");
|
||||
|
||||
|
||||
var key = this.rotateAnimation.createKeyFrame( 0 );
|
||||
|
||||
key.setProperty( "rotate", "3deg" );
|
||||
|
||||
|
||||
var key = this.rotateAnimation.createKeyFrame( 50 );
|
||||
|
||||
key.setProperty( "rotate", "-3deg" );
|
||||
|
||||
|
||||
var key = this.rotateAnimation.createKeyFrame( 100 );
|
||||
|
||||
key.setProperty( "rotate", "3deg" );
|
||||
|
||||
|
||||
|
||||
this.opacityAnimation = this.createAnimation("opacityAnimation");
|
||||
|
||||
this.opacityAnimation.setIterationCount("1");
|
||||
|
||||
this.opacityAnimation.setDuration( "0.9s" );
|
||||
|
||||
this.opacityAnimation.setFillMode( "forwards" );
|
||||
|
||||
|
||||
|
||||
|
||||
var key = this.opacityAnimation.createKeyFrame( 0 );
|
||||
|
||||
key.setProperty( "opacity", "0" );
|
||||
key.setProperty( "display", "none" );
|
||||
|
||||
|
||||
var key = this.opacityAnimation.createKeyFrame( 1 );
|
||||
|
||||
key.setProperty( "opacity", "0" );
|
||||
key.setProperty( "display", "block" );
|
||||
|
||||
|
||||
var key = this.opacityAnimation.createKeyFrame( 100 );
|
||||
|
||||
key.setProperty( "display", "block" );
|
||||
key.setProperty( "opacity", "100%" );
|
||||
|
||||
}
|
||||
|
||||
|
||||
mouseover() {
|
||||
|
||||
this.border = "2px solid rgb(125 177 211)";
|
||||
|
||||
}
|
||||
|
||||
mouseleave() {
|
||||
|
||||
this.border = "2px solid #F7FAFC";
|
||||
|
||||
//this.rotateAnimation.stop();
|
||||
|
||||
}
|
||||
|
||||
async click() {
|
||||
|
||||
var previewWindow = this.parent.parent.previewWindow;
|
||||
|
||||
previewWindow.setTitle( this.value );
|
||||
|
||||
previewWindow.show("block");
|
||||
|
||||
previewWindow.center();
|
||||
|
||||
previewWindow.setImage( "/assets/uploads/" + this.value );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
|
||||
import draggable from '/elements/window/draggable.js';
|
||||
|
||||
import page from '/elements/page.js';
|
||||
|
||||
import windowHeader from '/elements/window/header.js';
|
||||
|
||||
import previewImage from './preview/previewWindow.image.js';
|
||||
|
||||
|
||||
export default class imagePreviewWindow extends draggable{
|
||||
|
||||
selector = "#application";
|
||||
|
||||
backdropFilter = "blur(22px)";
|
||||
|
||||
paddingBottom = 30;
|
||||
|
||||
display = "none"
|
||||
|
||||
|
||||
#ifdef WINDOWS
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
background = "rgb(255 255 255 / 75%)"
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef DARK
|
||||
|
||||
background = "#202020cc"
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
create() {
|
||||
|
||||
this.center();
|
||||
|
||||
this.hide();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
width = 600;
|
||||
|
||||
|
||||
flexDirection = "column";
|
||||
|
||||
borderRadius = 12;
|
||||
|
||||
windowHeader = new windowHeader();
|
||||
|
||||
previewImage = new previewImage();
|
||||
|
||||
setTitle( title ) {
|
||||
|
||||
this.windowHeader.setTitle( title );
|
||||
}
|
||||
|
||||
setImage( path ) {
|
||||
|
||||
this.previewImage.setImage( path )
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
44
application/demo/fileManager/fileManager.js
Normal file
44
application/demo/fileManager/fileManager.js
Normal file
@@ -0,0 +1,44 @@
|
||||
|
||||
|
||||
import fileupload from './fileManager.upload.js';
|
||||
|
||||
import fileList from "./fileManager.list.js";
|
||||
|
||||
import removeIcons from "./fileManager.removeIcons.js";
|
||||
|
||||
import header from '/elements/header.js';
|
||||
|
||||
import page from '/elements/page.js';
|
||||
|
||||
import previewWindow from "./fileManager.imagePreviewWindow.js"
|
||||
|
||||
import fileChooser from '/elements/fileChooser/fileChooser.js';
|
||||
|
||||
|
||||
export default class fileManager extends page{
|
||||
|
||||
|
||||
width = "100%"
|
||||
|
||||
minHeight = 350;
|
||||
|
||||
flexDirection = "column"
|
||||
|
||||
uploadHeader = new header("Upload");
|
||||
|
||||
fileupload = new fileupload();
|
||||
|
||||
filesHeader = new header("Files");
|
||||
|
||||
removeIcons = new removeIcons();
|
||||
|
||||
fileList = new fileList();
|
||||
|
||||
previewWindow = new previewWindow();
|
||||
|
||||
|
||||
fileChooser = new fileChooser();
|
||||
|
||||
|
||||
|
||||
}
|
||||
55
application/demo/fileManager/fileManager.list.js
Normal file
55
application/demo/fileManager/fileManager.list.js
Normal file
@@ -0,0 +1,55 @@
|
||||
|
||||
|
||||
import fileIcon from "./fileManager.icon.js";
|
||||
|
||||
import panel from "/elements/panel/row.js";
|
||||
|
||||
#ifdef SERVER
|
||||
|
||||
import fs from "fs";
|
||||
|
||||
import path from "path";
|
||||
|
||||
#endif
|
||||
|
||||
export default class fileList extends panel{
|
||||
|
||||
margin = 20;
|
||||
|
||||
padding = 20;
|
||||
|
||||
display = "block";
|
||||
|
||||
async create() {
|
||||
|
||||
this.empty();
|
||||
|
||||
var files = await this.readFiles();
|
||||
|
||||
}
|
||||
|
||||
node async readFiles() {
|
||||
|
||||
var absolutePath = path.resolve( "./assets/uploads/" );
|
||||
|
||||
var files = fs.readdirSync( absolutePath );
|
||||
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
|
||||
var file = files[i];
|
||||
|
||||
|
||||
var currentFileIcon = new fileIcon();
|
||||
|
||||
currentFileIcon.value = file;
|
||||
|
||||
this.add( currentFileIcon );
|
||||
|
||||
|
||||
}
|
||||
|
||||
return files;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
74
application/demo/fileManager/fileManager.removeIcons.js
Normal file
74
application/demo/fileManager/fileManager.removeIcons.js
Normal file
@@ -0,0 +1,74 @@
|
||||
|
||||
import icon from "/elements/icon.js";
|
||||
|
||||
|
||||
export default class removeIcons extends icon{
|
||||
|
||||
width = 14;
|
||||
|
||||
height = 14;
|
||||
|
||||
margin = 4;
|
||||
|
||||
propegateEvent = false;
|
||||
|
||||
backgroundSize = "contain!important"
|
||||
|
||||
|
||||
cursor = "pointer";
|
||||
|
||||
boxMarginTop = "17px";
|
||||
|
||||
boxBorderRadius = 14;
|
||||
|
||||
boxBackground = "#ffffffbf";
|
||||
|
||||
boxWidth = "fit-content";
|
||||
|
||||
boxPadding = 2;
|
||||
|
||||
//boxPosition = "";
|
||||
|
||||
boxMarginLeft = 11;
|
||||
|
||||
boxMarginBottom = -37;
|
||||
|
||||
|
||||
constructor() {
|
||||
|
||||
super("edit.svg");
|
||||
|
||||
}
|
||||
|
||||
mode = "normal";
|
||||
|
||||
click() {
|
||||
|
||||
var icons = this.parent.fileList.getChildren();
|
||||
|
||||
for (var i = 0; i < icons.length; i++) {
|
||||
|
||||
var icon = icons[i];
|
||||
|
||||
icon.toggleEditMode();
|
||||
|
||||
}
|
||||
|
||||
|
||||
if(this.mode == "normal") {
|
||||
|
||||
this.setImage("/assets/images/icons/stop.png")
|
||||
|
||||
this.mode = "wiggle";
|
||||
|
||||
} else {
|
||||
|
||||
this.mode = "normal";
|
||||
|
||||
this.setImage("/assets/images/icons/edit.svg")
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
150
application/demo/fileManager/fileManager.upload.js
Normal file
150
application/demo/fileManager/fileManager.upload.js
Normal file
@@ -0,0 +1,150 @@
|
||||
|
||||
import fileUpload from "/elements/fileUpload.js";
|
||||
|
||||
import fileIcon from "./fileManager.icon.js";
|
||||
|
||||
|
||||
#ifdef SERVER
|
||||
|
||||
import fs from "fs";
|
||||
|
||||
import path from "path";
|
||||
|
||||
//import android from 'androidjs';
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
export default class stream extends fileUpload {
|
||||
|
||||
placeholder = "Upload."
|
||||
|
||||
|
||||
|
||||
margin = 20;
|
||||
|
||||
stream;
|
||||
|
||||
type;
|
||||
/*
|
||||
inputType = "button";
|
||||
|
||||
|
||||
|
||||
click( event ) {
|
||||
|
||||
//this.android_file_chooser();
|
||||
|
||||
|
||||
//var fileChooser = this.parent.fileChooser;
|
||||
|
||||
//fileChooser.show("flex")
|
||||
|
||||
//fileChooser.open();
|
||||
|
||||
}
|
||||
*/
|
||||
async change( event ) {
|
||||
|
||||
var input = this.customElement;
|
||||
|
||||
var files = input.files;
|
||||
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
|
||||
var file = files[i];
|
||||
|
||||
var chunksize = 64 * 1024;
|
||||
|
||||
var offset = 0;
|
||||
|
||||
var filename = file.name.replaceAll(" ", "_");
|
||||
|
||||
await this.createStream( filename );
|
||||
|
||||
|
||||
while( offset < file.size ) {
|
||||
|
||||
const chunkfile = await file.slice( offset, offset + chunksize );
|
||||
|
||||
const chunk = await chunkfile.arrayBuffer();
|
||||
|
||||
var intChunk = new Int8Array( chunk );
|
||||
|
||||
this.writeChunk( intChunk )
|
||||
|
||||
offset += chunksize;
|
||||
|
||||
}
|
||||
|
||||
await this.endstream();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
node async createStream( filename ) {
|
||||
|
||||
var absolutePath = path.resolve( "./assets/uploads/" + filename );
|
||||
|
||||
this.filename = filename;
|
||||
|
||||
console.log("Writing file to path", absolutePath);
|
||||
|
||||
this.stream = fs.createWriteStream( absolutePath, { encoding: 'binary' } );
|
||||
|
||||
this.stream.on('finish', function() {
|
||||
|
||||
console.log('file has been written');
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
node async writeChunk( chunk ) {
|
||||
|
||||
this.stream.write( Buffer.from( Object.values( chunk ) ) );
|
||||
|
||||
}
|
||||
|
||||
node async endstream() {
|
||||
|
||||
this.stream.end();
|
||||
|
||||
|
||||
var currentFileIcon = new fileIcon();
|
||||
|
||||
currentFileIcon.value = this.filename;
|
||||
|
||||
this.parent.fileList.add( currentFileIcon );
|
||||
|
||||
}
|
||||
|
||||
node async android_file_chooser() {
|
||||
|
||||
//const status = app.mobiledata.isEnabled();
|
||||
|
||||
//console.log(android);
|
||||
|
||||
//console.log("Mobile Data status: ", android);
|
||||
|
||||
//console.log(app.mobiledata.isEnabled());
|
||||
|
||||
}
|
||||
|
||||
|
||||
//mouseover() {
|
||||
|
||||
// console.log("mouseover??", this.parent.removeIcons)
|
||||
|
||||
// if( this.parent.removeIcons.mode == "wiggle" ) {
|
||||
|
||||
// this.parent.removeIcons.click();
|
||||
|
||||
// }
|
||||
|
||||
//}
|
||||
|
||||
|
||||
}
|
||||
24
application/demo/fileManager/preview/previewWindow.image.js
Normal file
24
application/demo/fileManager/preview/previewWindow.image.js
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
import image from "/elements/image.js";
|
||||
|
||||
|
||||
export default class previewImage extends image{
|
||||
|
||||
layers = 1;
|
||||
|
||||
width = "90%"
|
||||
|
||||
//height = "100%"
|
||||
|
||||
margin = "0 auto";
|
||||
|
||||
backgroundSize = "contain!important";
|
||||
|
||||
propegateEvent = false;
|
||||
|
||||
borderRadius = 12;
|
||||
|
||||
transition = "1s"
|
||||
|
||||
maxHeight = "87vh"
|
||||
}
|
||||
51
application/demo/leftSide/leftSide.button.animations.js
Normal file
51
application/demo/leftSide/leftSide.button.animations.js
Normal file
@@ -0,0 +1,51 @@
|
||||
|
||||
|
||||
import menuButton from './leftSide.button.js';
|
||||
|
||||
export default class settingsButton extends menuButton{
|
||||
|
||||
text = "Animations";
|
||||
|
||||
create() {
|
||||
|
||||
//this.activateButton();
|
||||
|
||||
}
|
||||
|
||||
touchstart() {
|
||||
|
||||
this.click();
|
||||
|
||||
}
|
||||
|
||||
async click() {
|
||||
|
||||
this.stateMachine.composeState( "Animations" );
|
||||
|
||||
this.openPage();
|
||||
|
||||
|
||||
}
|
||||
|
||||
state openPage() {
|
||||
|
||||
var application = this.parent.parent;
|
||||
|
||||
var rightSide = application.rightSide;
|
||||
var menu = application.leftSide;
|
||||
|
||||
this.deactivateButtons();
|
||||
this.activateButton();
|
||||
|
||||
rightSide.hideChildren();
|
||||
rightSide.animations.show();
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
application.minimizeButton.close();
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
51
application/demo/leftSide/leftSide.button.fileManager.js
Normal file
51
application/demo/leftSide/leftSide.button.fileManager.js
Normal file
@@ -0,0 +1,51 @@
|
||||
|
||||
|
||||
import menuButton from './leftSide.button.js';
|
||||
|
||||
export default class fileMangerButton extends menuButton{
|
||||
|
||||
text = "File Manager";
|
||||
|
||||
create() {
|
||||
|
||||
//this.activateButton();
|
||||
|
||||
}
|
||||
|
||||
touchstart() {
|
||||
|
||||
this.click();
|
||||
|
||||
}
|
||||
|
||||
async click() {
|
||||
|
||||
this.stateMachine.composeState( "File Manager" );
|
||||
|
||||
this.openPage();
|
||||
|
||||
}
|
||||
|
||||
state openPage() {
|
||||
|
||||
var application = this.parent.parent;
|
||||
|
||||
var rightSide = application.rightSide;
|
||||
|
||||
this.deactivateButtons();
|
||||
this.activateButton();
|
||||
|
||||
rightSide.hideChildren();
|
||||
rightSide.fileManager.show();
|
||||
|
||||
rightSide.fileManager.fileList.create();
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
application.minimizeButton.close();
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
51
application/demo/leftSide/leftSide.button.grid.js
Normal file
51
application/demo/leftSide/leftSide.button.grid.js
Normal file
@@ -0,0 +1,51 @@
|
||||
|
||||
|
||||
import menuButton from './leftSide.button.js';
|
||||
|
||||
export default class settingsButton extends menuButton{
|
||||
|
||||
text = "Grids";
|
||||
|
||||
create() {
|
||||
|
||||
//this.activateButton();
|
||||
|
||||
}
|
||||
|
||||
touchstart() {
|
||||
|
||||
this.click();
|
||||
|
||||
}
|
||||
|
||||
async click() {
|
||||
|
||||
this.stateMachine.composeState( "Appearance" );
|
||||
|
||||
this.openPage();
|
||||
|
||||
}
|
||||
|
||||
state openPage() {
|
||||
|
||||
var application = this.parent.parent;
|
||||
|
||||
var rightSide = application.rightSide;
|
||||
var menu = application.leftSide;
|
||||
|
||||
this.deactivateButtons();
|
||||
this.activateButton();
|
||||
|
||||
rightSide.hideChildren();
|
||||
rightSide.gridExample.show();
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
application.minimizeButton.close();
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
202
application/demo/leftSide/leftSide.button.js
Normal file
202
application/demo/leftSide/leftSide.button.js
Normal file
@@ -0,0 +1,202 @@
|
||||
export default class menuButton{
|
||||
|
||||
width = 110;
|
||||
|
||||
color;
|
||||
|
||||
background;
|
||||
|
||||
fontWeight;
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
width = "80vw";
|
||||
|
||||
borderRadius = 18;
|
||||
|
||||
textAlign = "center"
|
||||
|
||||
padding = 20;
|
||||
|
||||
margin = "0 auto"
|
||||
|
||||
marginTop = 8;
|
||||
|
||||
fontSize = 20;
|
||||
|
||||
height = 24;
|
||||
|
||||
fontWeight = "600"
|
||||
|
||||
fontColor = "#313131";
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
borderBottom = "1px solid #ececec";
|
||||
|
||||
background = "white";
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef WINDOWS
|
||||
|
||||
borderRadius = 6;
|
||||
|
||||
width = 160
|
||||
|
||||
padding = 10;
|
||||
|
||||
marginTop = 2;
|
||||
|
||||
marginBottom = 2;
|
||||
|
||||
paddingLeft = 30;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
activated = false;
|
||||
|
||||
propegateEvent = false;
|
||||
|
||||
cursor = "pointer";
|
||||
|
||||
#ifdef WINDOWS
|
||||
|
||||
#ifdef DARK
|
||||
|
||||
hightlightBackgroundColor = "#2d2d2d";
|
||||
|
||||
hightlightBackgroundColor = "#0c0e165c";
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
hightlightBackgroundColor = "rgb(141 180 189 / 12%)";
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MACOS
|
||||
|
||||
borderRadius = 8;
|
||||
|
||||
padding = 10;
|
||||
|
||||
paddingLeft = 20;
|
||||
|
||||
fontSize = 16;
|
||||
|
||||
fontWeight = "600";
|
||||
|
||||
|
||||
hightlightBackgroundColor = "rgb(189 193 221 / 22%)"
|
||||
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
color = "#343434";
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
activated = false;
|
||||
|
||||
state activateButton() {
|
||||
|
||||
this.activated = true;
|
||||
|
||||
this.highlightButton();
|
||||
|
||||
}
|
||||
|
||||
state deactivateButton() {
|
||||
|
||||
this.activated = false;
|
||||
|
||||
this.lowlightButton();
|
||||
|
||||
}
|
||||
|
||||
highlightButton() {
|
||||
|
||||
this.background = this.hightlightBackgroundColor;
|
||||
|
||||
if( !this.activated ) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
lowlightButton() {
|
||||
|
||||
if( !this.activated ) {
|
||||
|
||||
this.background = "";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
mouseover() {
|
||||
|
||||
this.highlightButton();
|
||||
|
||||
}
|
||||
|
||||
mouseleave() {
|
||||
|
||||
if( !this.activated ) {
|
||||
|
||||
this.lowLightButtons();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
deactivateButtons() {
|
||||
|
||||
var children = this.parent.getChildren();
|
||||
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
|
||||
var child = children[i];
|
||||
|
||||
if( child.deactivateButton ) {
|
||||
|
||||
child.deactivateButton();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
lowLightButtons() {
|
||||
|
||||
var children = this.parent.getChildren();
|
||||
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
|
||||
var child = children[i];
|
||||
|
||||
if( child.lowlightButton ){
|
||||
|
||||
child.lowlightButton();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
74
application/demo/leftSide/leftSide.button.news.js
Normal file
74
application/demo/leftSide/leftSide.button.news.js
Normal file
@@ -0,0 +1,74 @@
|
||||
|
||||
|
||||
import menuButton from './leftSide.button.js';
|
||||
|
||||
export default class newsButton extends menuButton{
|
||||
|
||||
text = "Home";
|
||||
|
||||
#ifdef MACOS
|
||||
|
||||
borderTopLeftRadius = 8;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
create() {
|
||||
|
||||
var pathName = window.location.pathname;
|
||||
|
||||
var pathParts = pathName.split("/");
|
||||
|
||||
if( !pathParts[1] ) {
|
||||
|
||||
this.stateMachine.composeState( );
|
||||
|
||||
}
|
||||
|
||||
//this.activateButton();
|
||||
|
||||
this.openPage();
|
||||
|
||||
}
|
||||
|
||||
async click() {
|
||||
|
||||
this.stateMachine.composeState( "Home" );
|
||||
|
||||
this.openPage();
|
||||
|
||||
}
|
||||
|
||||
state openPage() {
|
||||
|
||||
var application = this.parent.parent;
|
||||
|
||||
var rightSide = application.rightSide;
|
||||
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
application.minimizeButton.close();
|
||||
|
||||
#endif
|
||||
|
||||
this.deactivateButtons();
|
||||
|
||||
//this.activateButton();
|
||||
|
||||
console.log("rightSide", rightSide);
|
||||
|
||||
rightSide.newsPages.newsItemPage.transform = "translateX(0)";
|
||||
|
||||
rightSide.newsPages.newsPage.transform = "translateX(0)";
|
||||
|
||||
rightSide.hideChildren();
|
||||
|
||||
rightSide.newsPages.show();
|
||||
|
||||
//rightSide.newsPages.newsItemPage.newsListTable.body.sync()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
51
application/demo/leftSide/leftSide.button.settings.js
Normal file
51
application/demo/leftSide/leftSide.button.settings.js
Normal file
@@ -0,0 +1,51 @@
|
||||
|
||||
|
||||
import menuButton from './leftSide.button.js';
|
||||
|
||||
export default class settingsButton extends menuButton{
|
||||
|
||||
text = "Appearance";
|
||||
|
||||
create() {
|
||||
|
||||
//this.activateButton();
|
||||
|
||||
}
|
||||
|
||||
touchstart() {
|
||||
|
||||
this.click();
|
||||
|
||||
}
|
||||
|
||||
async click() {
|
||||
|
||||
this.stateMachine.composeState( "Appearance" );
|
||||
|
||||
this.openPage();
|
||||
|
||||
}
|
||||
|
||||
state openPage() {
|
||||
|
||||
var application = this.parent.parent;
|
||||
|
||||
var rightSide = application.rightSide;
|
||||
var menu = application.leftSide;
|
||||
|
||||
this.deactivateButtons();
|
||||
this.activateButton();
|
||||
|
||||
rightSide.hideChildren();
|
||||
rightSide.settings.show();
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
application.minimizeButton.close();
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
62
application/demo/leftSide/leftSide.button.signin.js
Normal file
62
application/demo/leftSide/leftSide.button.signin.js
Normal file
@@ -0,0 +1,62 @@
|
||||
|
||||
|
||||
import menuButton from './leftSide.button.js';
|
||||
|
||||
import groups from '/user/group/user.group.permission.js';
|
||||
|
||||
|
||||
export default class signinPageButton extends menuButton{
|
||||
|
||||
text = "Signin";
|
||||
|
||||
click() {
|
||||
|
||||
this.stateMachine.composeState( "Signin" );
|
||||
|
||||
this.openPage();
|
||||
|
||||
}
|
||||
|
||||
state openPage() {
|
||||
|
||||
this.deactivateButtons();
|
||||
this.activateButton();
|
||||
|
||||
var application = this.parent.parent;
|
||||
|
||||
var rightSide = application.rightSide;
|
||||
|
||||
rightSide.width = "";
|
||||
|
||||
rightSide.hideChildren();
|
||||
|
||||
rightSide.signin.show();
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
application.minimizeButton.close();
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
enableREAD() {
|
||||
|
||||
this.hide();
|
||||
|
||||
}
|
||||
|
||||
disableREAD() {
|
||||
|
||||
this.show();
|
||||
|
||||
}
|
||||
|
||||
permission() {
|
||||
|
||||
this.allow( groups.member , "READ" );
|
||||
this.allow( groups.admin , "READ" );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
94
application/demo/leftSide/leftSide.button.signout.js
Normal file
94
application/demo/leftSide/leftSide.button.signout.js
Normal file
@@ -0,0 +1,94 @@
|
||||
|
||||
|
||||
import menuButton from './leftSide.button.js';
|
||||
|
||||
import groups from '/user/group/user.group.permission.js';
|
||||
|
||||
import userManager from '/server/userManager.js';
|
||||
|
||||
|
||||
export default class signoutButton extends menuButton{
|
||||
|
||||
text = "Signout";
|
||||
|
||||
create(){
|
||||
|
||||
this.hide();
|
||||
|
||||
}
|
||||
|
||||
async click() {
|
||||
|
||||
this.stateMachine.composeState( "Signout" );
|
||||
|
||||
await this.openPage();
|
||||
|
||||
}
|
||||
|
||||
state async openPage() {
|
||||
|
||||
this.deactivateButtons();
|
||||
this.activateButton();
|
||||
|
||||
console.log("before process",this);
|
||||
|
||||
var visitorUser = await this.signout();
|
||||
|
||||
|
||||
localStorage.setItem( "username", false );
|
||||
|
||||
localStorage.setItem( "sessionKey", false );
|
||||
|
||||
this.getCore().updatePermissions( visitorUser.permissionObjects );
|
||||
|
||||
}
|
||||
|
||||
createVisitor( client ) {
|
||||
|
||||
var userInstance = new user();
|
||||
|
||||
userInstance.username.value = "Visitor";
|
||||
|
||||
userInstance.id = 0;
|
||||
|
||||
userInstance.permissionObjects = userManager.getPermissions( userInstance, client );
|
||||
|
||||
return userInstance;
|
||||
|
||||
}
|
||||
|
||||
node async signout( object, client ) {
|
||||
|
||||
var newUser = this.createVisitor( this.client );
|
||||
|
||||
this.client.user = newUser;
|
||||
|
||||
global.core.setUserObjects( false, this.client );
|
||||
|
||||
return newUser;
|
||||
|
||||
}
|
||||
|
||||
enableREAD() {
|
||||
|
||||
this.show();
|
||||
|
||||
}
|
||||
|
||||
disableREAD() {
|
||||
|
||||
this.hide();
|
||||
|
||||
}
|
||||
|
||||
permission() {
|
||||
|
||||
this.allow( groups.member , "PROCESS" );
|
||||
this.allow( groups.admin , "PROCESS" );
|
||||
|
||||
this.allow( groups.member , "READ" );
|
||||
this.allow( groups.admin , "READ" );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
37
application/demo/leftSide/leftSide.button.signup.js
Normal file
37
application/demo/leftSide/leftSide.button.signup.js
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
import menuButton from './leftSide.button.js';
|
||||
|
||||
export default class signinButton extends menuButton{
|
||||
|
||||
text = "Signup";
|
||||
|
||||
click() {
|
||||
|
||||
this.stateMachine.composeState( "Signup" );
|
||||
|
||||
this.openPage();
|
||||
|
||||
}
|
||||
|
||||
state openPage() {
|
||||
|
||||
this.deactivateButtons();
|
||||
this.activateButton();
|
||||
|
||||
var application = this.parent.parent;
|
||||
|
||||
var rightSide = application.rightSide;
|
||||
|
||||
rightSide.hideChildren();
|
||||
|
||||
rightSide.signup.show();
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
application.minimizeButton.close();
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
68
application/demo/leftSide/leftSide.button.users.js
Normal file
68
application/demo/leftSide/leftSide.button.users.js
Normal file
@@ -0,0 +1,68 @@
|
||||
|
||||
|
||||
import menuButton from './leftSide.button.js';
|
||||
|
||||
import groups from '/user/group/user.group.permission.js';
|
||||
|
||||
|
||||
export default class usersPageButton extends menuButton{
|
||||
|
||||
text = "Users";
|
||||
|
||||
click() {
|
||||
|
||||
this.stateMachine.composeState( "Users" );
|
||||
|
||||
this.openPage();
|
||||
|
||||
}
|
||||
|
||||
state openPage() {
|
||||
|
||||
this.deactivateButtons();
|
||||
|
||||
this.activateButton();
|
||||
|
||||
var application = this.parent.parent;
|
||||
|
||||
var rightSide = application.rightSide;
|
||||
|
||||
rightSide.width = "";
|
||||
|
||||
rightSide.hideChildren();
|
||||
|
||||
rightSide.userListPage.show();
|
||||
|
||||
rightSide.userListPage.userTable.body.create()
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
application.minimizeButton.close();
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
enableREAD() {
|
||||
|
||||
this.show();
|
||||
|
||||
}
|
||||
|
||||
disableREAD() {
|
||||
|
||||
this.hide();
|
||||
|
||||
}
|
||||
|
||||
permission() {
|
||||
|
||||
this.allow( groups.member , "PROCESS" );
|
||||
this.allow( groups.admin , "PROCESS" );
|
||||
|
||||
this.allow( groups.member , "READ" );
|
||||
this.allow( groups.admin , "READ" );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
33
application/demo/leftSide/leftSide.header.js
Normal file
33
application/demo/leftSide/leftSide.header.js
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
import header from "/elements/header.js";
|
||||
|
||||
export default class menuHeader extends header{
|
||||
|
||||
text = "Menu";
|
||||
|
||||
flexDirection = "column";
|
||||
|
||||
fontSize = 36;
|
||||
|
||||
paddingTop = 100;
|
||||
|
||||
fontWeight = "300"
|
||||
|
||||
paddingBottom = 100;
|
||||
|
||||
textAlign = "center";
|
||||
|
||||
width = "100vw";
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
display = "flex";
|
||||
|
||||
#else
|
||||
|
||||
display = "none";
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
179
application/demo/leftSide/leftSide.js
Normal file
179
application/demo/leftSide/leftSide.js
Normal file
@@ -0,0 +1,179 @@
|
||||
|
||||
|
||||
import button from '/elements/button.js';
|
||||
|
||||
import newsButton from './leftSide.button.news.js';
|
||||
|
||||
import settingsButton from './leftSide.button.settings.js';
|
||||
|
||||
import signinButton from './leftSide.button.signin.js';
|
||||
|
||||
import signupButton from './leftSide.button.signup.js';
|
||||
|
||||
import signoutButton from './leftSide.button.signout.js';
|
||||
|
||||
import fileManagerButton from './leftSide.button.fileManager.js';
|
||||
|
||||
import animationsButton from './leftSide.button.animations.js';
|
||||
|
||||
import usersButton from './leftSide.button.users.js';
|
||||
|
||||
import gridButton from './leftSide.button.grid.js';
|
||||
|
||||
import menuHeader from './leftSide.header.js';
|
||||
|
||||
|
||||
|
||||
export default class leftSide{
|
||||
|
||||
state = "visible";
|
||||
|
||||
boxOverflow = "hidden";
|
||||
|
||||
boxTransition = "0.3S";
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
boxHeight = "100vh";
|
||||
|
||||
#else
|
||||
|
||||
boxHeight = "";
|
||||
|
||||
#endif
|
||||
|
||||
boxWidth = 220;
|
||||
|
||||
width = 220;
|
||||
|
||||
flexDirection = "column";
|
||||
|
||||
//borderRight = "1px solid #3D3D3D"
|
||||
paddingTop = 30;
|
||||
|
||||
//minHeight = "40vh"
|
||||
|
||||
header = new menuHeader();
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
height = "100vh";
|
||||
|
||||
paddingTop = "";
|
||||
|
||||
boxWidth = "100vw";
|
||||
|
||||
width = "100vw";
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
boxBackground = "#f2f2f2";
|
||||
|
||||
//background = "white";
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef WINDOWS
|
||||
|
||||
paddingLeft = 4;
|
||||
|
||||
paddingRight = 4;
|
||||
|
||||
#ifdef DARK
|
||||
|
||||
background = "#202020cc";
|
||||
|
||||
color = "white"
|
||||
|
||||
borderRight = "1px solid black"
|
||||
|
||||
fontWeight = "500";
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
background = "rgb(255 255 255 / 75%)";
|
||||
|
||||
color = "black";
|
||||
|
||||
|
||||
fontWeight = "200";
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef MACOS
|
||||
|
||||
paddingTop = 40;
|
||||
|
||||
paddingLeft = 20;
|
||||
|
||||
|
||||
#ifdef DARK
|
||||
|
||||
background = "rgb(40 22 22 / 75%)";
|
||||
|
||||
color = "white"
|
||||
|
||||
borderRight = "1px solid black"
|
||||
|
||||
fontWeight = "bold";
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
background = "rgb(255 255 255 / 75%)";
|
||||
|
||||
color = "black";
|
||||
|
||||
|
||||
fontWeight = "200";
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
render() {
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
//opacity = "90%";
|
||||
|
||||
//backdropFilter = "blur(20px)";
|
||||
|
||||
borderTopLeftRadius = 8;
|
||||
|
||||
borderBottomLeftRadius = 8;
|
||||
|
||||
borderTopLeftRadius = "12px";
|
||||
|
||||
borderBottomLeftRadius = "12px";
|
||||
|
||||
|
||||
|
||||
newsButton = new newsButton();
|
||||
|
||||
settingsButton = new settingsButton();
|
||||
|
||||
usersButton = new usersButton();
|
||||
|
||||
signinButton = new signinButton();
|
||||
|
||||
signoutButton = new signoutButton();
|
||||
|
||||
signupButton = new signupButton();
|
||||
|
||||
animationsButton = new animationsButton();
|
||||
|
||||
gridButton = new gridButton();
|
||||
|
||||
fileManagerButton = new fileManagerButton();
|
||||
|
||||
}
|
||||
21
application/demo/leftSide/testChild.js
Normal file
21
application/demo/leftSide/testChild.js
Normal file
@@ -0,0 +1,21 @@
|
||||
export default class testChild{
|
||||
|
||||
text = "test";
|
||||
|
||||
width = 160;
|
||||
|
||||
height = 30;
|
||||
|
||||
background = "red";
|
||||
|
||||
|
||||
fontSize = 24;
|
||||
|
||||
constructor( abc ) {
|
||||
|
||||
this.text = abc;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
29
application/demo/list/header/news.list.header.actions.js
Normal file
29
application/demo/list/header/news.list.header.actions.js
Normal file
@@ -0,0 +1,29 @@
|
||||
|
||||
import groups from '/user/group/user.group.permission.js';
|
||||
|
||||
import gridViewColumn from '/elements/gridView/gridView.header.row.column.js';
|
||||
|
||||
|
||||
export default class newsListHeaderActions extends gridViewColumn {
|
||||
|
||||
text = "Actions";
|
||||
|
||||
enableDELETE() {
|
||||
|
||||
this.show();
|
||||
|
||||
}
|
||||
|
||||
disableDELETE() {
|
||||
|
||||
this.hide();
|
||||
|
||||
}
|
||||
|
||||
permission() {
|
||||
|
||||
this.allow( groups.admin, "DELETE" );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
9
application/demo/list/header/news.list.header.body.js
Normal file
9
application/demo/list/header/news.list.header.body.js
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
|
||||
import gridViewColumn from '/elements/gridView/gridView.header.row.column.js';
|
||||
|
||||
export default class newsListHeaderBody extends gridViewColumn{
|
||||
|
||||
text = "Message";
|
||||
|
||||
}
|
||||
26
application/demo/list/header/news.list.header.js
Normal file
26
application/demo/list/header/news.list.header.js
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
|
||||
import news from '../../news.js';
|
||||
|
||||
import body from './news.list.header.body.js';
|
||||
|
||||
import title from './news.list.header.title.js';
|
||||
|
||||
import price from './news.list.header.price.js';
|
||||
|
||||
import actions from './news.list.header.actions.js';
|
||||
|
||||
import gridViewRow from '/elements/gridView/gridView.header.row.js';
|
||||
|
||||
|
||||
export default class newsListHeader extends news, gridViewRow {
|
||||
|
||||
body = new body();
|
||||
|
||||
title = new title();
|
||||
|
||||
price = new price();
|
||||
|
||||
actions = new actions();
|
||||
|
||||
}
|
||||
10
application/demo/list/header/news.list.header.price.js
Normal file
10
application/demo/list/header/news.list.header.price.js
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
|
||||
import gridViewColumn from '/elements/gridView/gridView.header.row.column.js';
|
||||
|
||||
|
||||
export default class newsListHeaderPrice extends gridViewColumn{
|
||||
|
||||
text = "Price";
|
||||
|
||||
}
|
||||
12
application/demo/list/header/news.list.header.title.js
Normal file
12
application/demo/list/header/news.list.header.title.js
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
|
||||
import newsTitle from '../../news.title.js';
|
||||
|
||||
import gridViewColumn from '/elements/gridView/gridView.header.row.column.js';
|
||||
|
||||
|
||||
export default class newsListHeaderTitle extends gridViewColumn{
|
||||
|
||||
text = "Title";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
|
||||
import button from "/elements/button.js";
|
||||
|
||||
import groups from '/user/group/user.group.permission.js';
|
||||
|
||||
export default class deleteButton extends button {
|
||||
|
||||
text = "Delete";
|
||||
|
||||
propegateEvent = false;
|
||||
|
||||
|
||||
async click() {
|
||||
|
||||
var sure = confirm("Are you sure you want to delete this item");
|
||||
|
||||
if( sure ) {
|
||||
|
||||
await this.parent.parent.delete();
|
||||
|
||||
this.parent.parent.remove();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
permission() {
|
||||
|
||||
this.allow( groups.admin, "DELETE" );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
23
application/demo/list/item/news.list.item.actions.js
Normal file
23
application/demo/list/item/news.list.item.actions.js
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
import gridViewColumn from '/elements/gridView/gridView.body.row.column.js';
|
||||
|
||||
import deleteButton from "./news.list.item.actions.deleteButton.js"
|
||||
|
||||
|
||||
export default class newsListItemActions extends gridViewColumn, gridViewColumn{
|
||||
|
||||
useCustomElement = false;
|
||||
|
||||
padding = 20;
|
||||
|
||||
display = "table-cell";
|
||||
|
||||
layers = 1;
|
||||
|
||||
paddingLeft = 30;
|
||||
|
||||
borderRadius;
|
||||
|
||||
deleteButton = new deleteButton();
|
||||
|
||||
}
|
||||
9
application/demo/list/item/news.list.item.body.js
Normal file
9
application/demo/list/item/news.list.item.body.js
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
import newsBody from '../../news.body.js';
|
||||
|
||||
import gridViewColumn from '/elements/gridView/gridView.body.row.column.js';
|
||||
|
||||
export default class newsListItemBody extends newsBody, gridViewColumn{
|
||||
|
||||
|
||||
}
|
||||
123
application/demo/list/item/news.list.item.js
Normal file
123
application/demo/list/item/news.list.item.js
Normal file
@@ -0,0 +1,123 @@
|
||||
import news from '../../news.js';
|
||||
|
||||
import body from './news.list.item.body.js';
|
||||
|
||||
import title from './news.list.item.title.js';
|
||||
|
||||
import price from './news.list.item.price.js';
|
||||
|
||||
import actions from './news.list.item.actions.js';
|
||||
|
||||
import groups from '/user/group/user.group.permission.js';
|
||||
|
||||
import gridViewRow from '/elements/gridView/gridView.body.row.js';
|
||||
|
||||
export default class newsListItem extends news, gridViewRow {
|
||||
|
||||
body = new body();
|
||||
|
||||
title = new title();
|
||||
|
||||
price = new price();
|
||||
|
||||
actions = new actions();
|
||||
|
||||
cursor = "pointer";
|
||||
|
||||
background;
|
||||
|
||||
#ifdef MACOS
|
||||
|
||||
fontSize = 14;
|
||||
|
||||
#endif
|
||||
|
||||
hoverBackgroundColor = "#363333"
|
||||
|
||||
#ifdef DARK
|
||||
|
||||
//mouseHoverColor = "#363333";
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
//mouseHoverColor = "rgb(255 255 255 / 95%)";
|
||||
|
||||
#endif
|
||||
|
||||
async click() {
|
||||
|
||||
this.stateMachine.composeState( this.id, this.value );
|
||||
|
||||
await this.loadPage( this.id );
|
||||
|
||||
}
|
||||
|
||||
state async loadPage( id ) {
|
||||
|
||||
var rightSide = this.parents("newsPages");
|
||||
|
||||
var boundBox = this.defaultElement.getBoundingClientRect();
|
||||
|
||||
var width = boundBox.width;
|
||||
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
rightSide.newsItemPage.translateX = -width;
|
||||
|
||||
#elif
|
||||
|
||||
rightSide.newsItemPage.transform = "translateX(-600px)";
|
||||
|
||||
rightSide.newsPage.transform = "translateX(-600px)";
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
var newsPage = rightSide.newsPage;
|
||||
|
||||
newsPage.id = this.id;
|
||||
|
||||
await newsPage.sync();
|
||||
|
||||
newsPage.createComment.create();
|
||||
|
||||
//newsPage.updateFrostglass();
|
||||
|
||||
//newsPage.show();
|
||||
|
||||
}
|
||||
|
||||
mouseover() {
|
||||
|
||||
//this.background = this.mouseHoverColor;
|
||||
|
||||
}
|
||||
|
||||
mouseleave() {
|
||||
|
||||
//this.background = "none";
|
||||
|
||||
}
|
||||
|
||||
enableDELETE() {
|
||||
|
||||
this.actions.show();
|
||||
|
||||
}
|
||||
|
||||
disableDELETE() {
|
||||
|
||||
this.actions.hide();
|
||||
|
||||
}
|
||||
|
||||
permission() {
|
||||
|
||||
this.allow( groups.admin, "DELETE" );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
20
application/demo/list/item/news.list.item.price.js
Normal file
20
application/demo/list/item/news.list.item.price.js
Normal file
@@ -0,0 +1,20 @@
|
||||
|
||||
import newsPrice from '../../news.price.js';
|
||||
|
||||
import gridViewColumn from '/elements/gridView/gridView.body.row.column.js';
|
||||
|
||||
|
||||
export default class newsListItemPrice extends newsPrice, gridViewColumn{
|
||||
|
||||
create() {
|
||||
|
||||
const formatter = new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'EUR',
|
||||
});
|
||||
|
||||
this.text = formatter.format( this.value );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
10
application/demo/list/item/news.list.item.title.js
Normal file
10
application/demo/list/item/news.list.item.title.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import newsTitle from '../../news.title.js';
|
||||
|
||||
import gridViewColumn from '/elements/gridView/gridView.body.row.column.js';
|
||||
|
||||
|
||||
export default class newsListItemTitle extends newsTitle, gridViewColumn{
|
||||
|
||||
|
||||
|
||||
}
|
||||
168
application/demo/list/news.list.table.body.js
Normal file
168
application/demo/list/news.list.table.body.js
Normal file
@@ -0,0 +1,168 @@
|
||||
|
||||
|
||||
import renderCollection from '/unify/renderCollection.js';
|
||||
|
||||
import groups from '/user/group/user.group.permission.js';
|
||||
|
||||
import gridViewBody from '/elements/gridView/gridView.body.js';
|
||||
|
||||
import OR from '/unify/sql/OR.js';
|
||||
|
||||
import AND from '/unify/sql/AND.js';
|
||||
|
||||
import LIKE from '/unify/sql/LIKE.js';
|
||||
|
||||
import GREATER from '/unify/sql/GREATER_OR_EQUAL.js';
|
||||
|
||||
import SMALLER from '/unify/sql/SMALLER_OR_EQUAL.js';
|
||||
|
||||
|
||||
export default class newsListTableBody extends renderCollection, gridViewBody {
|
||||
|
||||
debug = true;
|
||||
|
||||
sort = "title";
|
||||
|
||||
page = 0;
|
||||
|
||||
limit = 2;
|
||||
|
||||
async create() {
|
||||
|
||||
//this.update();
|
||||
|
||||
}
|
||||
|
||||
async update( updatePagination = true ) {
|
||||
|
||||
if( updatePagination ) {
|
||||
|
||||
this.page = 0;
|
||||
|
||||
}
|
||||
|
||||
this.numberOfPages = await this.filterSearch( this.searchType, this.searchTerm, this.sort, this.direction, this.limit, this.page );
|
||||
|
||||
await this.sync();
|
||||
|
||||
if( updatePagination ) {
|
||||
|
||||
this.parents("newsItemPage").tableControl.pagination.create();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
node async filterSearch( searchType, searchTerm, order, direction, limit, page ) {
|
||||
|
||||
console.log("searchType", searchType);
|
||||
|
||||
console.log("search input", searchTerm);
|
||||
|
||||
console.log("search order", order);
|
||||
|
||||
console.log("direction", direction);
|
||||
|
||||
console.log("limit", limit);
|
||||
|
||||
console.log("from", page * limit);
|
||||
|
||||
|
||||
|
||||
var filter = this.getFilter();
|
||||
|
||||
switch( searchType ) {
|
||||
|
||||
case 0:
|
||||
|
||||
filter.search = OR( OR( LIKE( filter.title, searchTerm ), LIKE( filter.comments.body, searchTerm ) ), LIKE( filter.body, searchTerm ) );
|
||||
|
||||
|
||||
break;
|
||||
|
||||
case 1:
|
||||
|
||||
filter.search = GREATER( filter.price, searchTerm );
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
|
||||
filter.search = SMALLER( filter.price, searchTerm );
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if( !searchTerm ) {
|
||||
|
||||
filter.search = false;
|
||||
|
||||
}
|
||||
|
||||
switch( order ) {
|
||||
|
||||
case "title":
|
||||
|
||||
filter.order = filter.title;
|
||||
|
||||
break;
|
||||
|
||||
case "body":
|
||||
|
||||
filter.order = filter.body;
|
||||
|
||||
break;
|
||||
|
||||
case "price":
|
||||
|
||||
filter.order = filter.price;
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if( direction == "desc" ) {
|
||||
|
||||
filter.direction = "desc";
|
||||
|
||||
} else {
|
||||
|
||||
filter.direction = "asc";
|
||||
|
||||
}
|
||||
|
||||
filter.limit = 1000;
|
||||
|
||||
filter.from = 0;
|
||||
|
||||
// See how many searched rows there are in total
|
||||
this.get();
|
||||
|
||||
filter.limit = parseInt( limit );
|
||||
|
||||
filter.from = parseInt( page * limit );
|
||||
|
||||
var numberOfPages = Math.ceil( this.rows.length / limit );
|
||||
|
||||
console.log("numberOfPages", numberOfPages);
|
||||
|
||||
console.log("this.rows.length", this.rows.length);
|
||||
|
||||
console.log("limit", limit);
|
||||
|
||||
return numberOfPages;
|
||||
|
||||
}
|
||||
|
||||
permission() {
|
||||
|
||||
this.allow( groups.visitor, "READ" );
|
||||
|
||||
this.allow( groups.member, "READ" );
|
||||
|
||||
this.allow( groups.admin, "READ" );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
13
application/demo/list/news.list.table.header.js
Normal file
13
application/demo/list/news.list.table.header.js
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
import newsListHeaderRow from "./header/news.list.header.js";
|
||||
|
||||
import document from '/unify/document.js';
|
||||
|
||||
import gridViewHeader from '/elements/gridView/gridView.header.js';
|
||||
|
||||
|
||||
export default class newsListTableHeader extends gridViewHeader {
|
||||
|
||||
newsListHeaderRow = new newsListHeaderRow();
|
||||
|
||||
}
|
||||
34
application/demo/list/news.list.table.js
Normal file
34
application/demo/list/news.list.table.js
Normal file
@@ -0,0 +1,34 @@
|
||||
|
||||
|
||||
import newsListTableHeader from "./news.list.table.header.js";
|
||||
|
||||
import newsListTableBody from "./news.list.table.body.js";
|
||||
|
||||
|
||||
import collection from '/unify/collection.js';
|
||||
|
||||
import newsListItem from '../list/item/news.list.item.js';
|
||||
|
||||
import news from '../news.js';
|
||||
|
||||
import gridView from '/elements/gridView/gridView.js';
|
||||
|
||||
|
||||
|
||||
export default class newsListTable extends gridView{
|
||||
|
||||
header = new newsListTableHeader();
|
||||
|
||||
body = new newsListTableBody( newsListItem, new collection( news ) );
|
||||
|
||||
#ifdef MACOS
|
||||
|
||||
marginLeft = 6;
|
||||
|
||||
width = "auto"
|
||||
|
||||
margin = "6px -2px 10px 6px"
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
122
application/demo/minimizeButton.js
Normal file
122
application/demo/minimizeButton.js
Normal file
@@ -0,0 +1,122 @@
|
||||
|
||||
import icon from '/elements/icon.js';
|
||||
|
||||
export default class minimizeButton {
|
||||
|
||||
//showFps = true;
|
||||
|
||||
text = "<"
|
||||
|
||||
color = "white"
|
||||
|
||||
fontWeight = "bold"
|
||||
|
||||
cursor = "pointer"
|
||||
|
||||
|
||||
|
||||
propegateEvent = false
|
||||
|
||||
zIndex = 5000
|
||||
|
||||
transition = "1s easeInOutQuart";
|
||||
|
||||
margin = 10;
|
||||
|
||||
|
||||
|
||||
position = "absolute";
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
top = "4px"
|
||||
|
||||
#else
|
||||
|
||||
bottom = "4px"
|
||||
|
||||
#endif
|
||||
|
||||
left = "0"
|
||||
|
||||
zIndex = "100000"
|
||||
|
||||
transform = "rotate(0)" ;
|
||||
|
||||
open() {
|
||||
|
||||
var menu = this.parent.leftSide;
|
||||
|
||||
//this.setImage("chevron-right.svg")
|
||||
|
||||
this.transform = "scale(1, 1)";
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
menu.boxHeight = "100vh";
|
||||
|
||||
#else
|
||||
|
||||
menu.boxWidth = 220;
|
||||
|
||||
#endif
|
||||
|
||||
menu.state = "visible";
|
||||
|
||||
this.parent.rightSide.borderRadius = "";
|
||||
|
||||
}
|
||||
|
||||
close() {
|
||||
|
||||
var menu = this.parent.leftSide;
|
||||
|
||||
//this.setImage("chevron-left.svg")
|
||||
|
||||
this.transform = "scale(-1, 1)";
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
menu.boxHeight = "0";
|
||||
|
||||
#else
|
||||
|
||||
menu.boxWidth = "0";
|
||||
|
||||
#endif
|
||||
|
||||
menu.state = "hidden";
|
||||
|
||||
var that = this;
|
||||
|
||||
setTimeout(function(){
|
||||
|
||||
console.log("asd");
|
||||
|
||||
that.parent.rightSide.borderRadius = 12;
|
||||
|
||||
|
||||
}, 1000)
|
||||
|
||||
}
|
||||
|
||||
click() {
|
||||
|
||||
var menu = this.parent.leftSide;
|
||||
|
||||
var state = menu.state;
|
||||
|
||||
if( state == "visible" ) {
|
||||
|
||||
this.close();
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
this.open();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
7
application/demo/news.body.js
Normal file
7
application/demo/news.body.js
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
import column from '/unify/column.js';
|
||||
|
||||
export default class body extends column{
|
||||
|
||||
|
||||
}
|
||||
29
application/demo/news.js
Normal file
29
application/demo/news.js
Normal file
@@ -0,0 +1,29 @@
|
||||
|
||||
import table from '/unify/table.js';
|
||||
|
||||
import title from './news.title.js';
|
||||
|
||||
import body from './news.body.js';
|
||||
|
||||
import price from './news.price.js';
|
||||
|
||||
import user from '/user/user.js';
|
||||
|
||||
import comment from './comment/comment.js';
|
||||
|
||||
import collection from '/unify/collection.js';
|
||||
|
||||
|
||||
|
||||
|
||||
export default class news extends table {
|
||||
|
||||
title = new title();
|
||||
|
||||
body = new body();
|
||||
|
||||
price = new price();
|
||||
|
||||
comments = new collection( comment );
|
||||
|
||||
}
|
||||
11
application/demo/news.price.js
Normal file
11
application/demo/news.price.js
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
import column from '/unify/column.js';
|
||||
|
||||
import datatype from '/unify/datatype.js';
|
||||
|
||||
|
||||
export default class price extends column{
|
||||
|
||||
datatype = datatype.REAL;
|
||||
|
||||
}
|
||||
9
application/demo/news.title.js
Normal file
9
application/demo/news.title.js
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
|
||||
import column from '/unify/column.js';
|
||||
|
||||
export default class title extends column {
|
||||
|
||||
|
||||
|
||||
}
|
||||
77
application/demo/page/news.page.backButton.js
Normal file
77
application/demo/page/news.page.backButton.js
Normal file
@@ -0,0 +1,77 @@
|
||||
|
||||
import icon from "/elements/icon.js";
|
||||
|
||||
export default class backButton{
|
||||
|
||||
text = "<"
|
||||
|
||||
color = "white"
|
||||
|
||||
fontWeight = "bold"
|
||||
|
||||
cursor = "pointer";
|
||||
|
||||
propegateEvent = false
|
||||
|
||||
transition = "2s";
|
||||
|
||||
margin = 10;
|
||||
|
||||
marginLeft = 20;
|
||||
|
||||
fontFamily = "unset"
|
||||
|
||||
|
||||
|
||||
#ifdef WINDOWS
|
||||
|
||||
#ifdef DARK
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MACOS
|
||||
|
||||
#ifdef DARK
|
||||
|
||||
boxBackground = "#282828";
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
boxBackground = "#ffffff";
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
click() {
|
||||
|
||||
this.stateMachine.composeState( "Home" );
|
||||
|
||||
this.openNewsItems();
|
||||
|
||||
}
|
||||
|
||||
state openNewsItems() {
|
||||
|
||||
var rightSide = this.parents("newsPages");
|
||||
|
||||
//rightSide.newsItemPage.marginLeft = "0";
|
||||
|
||||
rightSide.newsItemPage.transform = "translateX(0)";
|
||||
|
||||
rightSide.newsPage.transform = "translateX(0)";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
57
application/demo/page/news.page.body.js
Normal file
57
application/demo/page/news.page.body.js
Normal file
@@ -0,0 +1,57 @@
|
||||
|
||||
import newsBody from '../news.body.js';
|
||||
|
||||
import textarea from '/elements/textarea.js';
|
||||
|
||||
import flexbox from '/elements/flexbox.js';
|
||||
|
||||
export default class newsPageBody extends flexbox, newsBody{
|
||||
|
||||
padding = 20;
|
||||
|
||||
width = "-webkit-fill-available"
|
||||
|
||||
#ifdef MACOS
|
||||
|
||||
#ifdef DARK
|
||||
|
||||
background = "#282828";
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
background = "#ffffff";
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef WINDOWS
|
||||
|
||||
#ifdef DARK
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
borderRadius = "0 0 18px 18px"
|
||||
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
background = "white";
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
49
application/demo/page/news.page.edit.button.js
Normal file
49
application/demo/page/news.page.edit.button.js
Normal file
@@ -0,0 +1,49 @@
|
||||
|
||||
import button from "/elements/button.js";
|
||||
|
||||
export default class backButton extends button{
|
||||
|
||||
text = "Edit News"
|
||||
|
||||
color = "white"
|
||||
|
||||
fontWeight = "bold"
|
||||
|
||||
cursor = "pointer";
|
||||
|
||||
float = "right"
|
||||
|
||||
propegateEvent = false
|
||||
|
||||
transition = "2s";
|
||||
|
||||
margin = 10;
|
||||
|
||||
marginLeft = 20;
|
||||
|
||||
fontFamily = "unset"
|
||||
|
||||
|
||||
|
||||
click() {
|
||||
|
||||
this.stateMachine.composeState( "Edit" );
|
||||
|
||||
var rightSide = this.parents("newsPages");
|
||||
|
||||
//this.parent.hide();
|
||||
|
||||
var newsEdit = rightSide.newsEdit;
|
||||
|
||||
newsEdit.id = this.parent.id;
|
||||
|
||||
newsEdit.sync();
|
||||
|
||||
newsEdit.show();
|
||||
|
||||
//rightSide.newsPage.transform = "translateX(-1200px)";
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
254
application/demo/page/news.page.js
Normal file
254
application/demo/page/news.page.js
Normal file
@@ -0,0 +1,254 @@
|
||||
|
||||
import news from '../news.js';
|
||||
|
||||
import newsPageTitle from './news.page.title.js';
|
||||
|
||||
import newsPageBody from './news.page.body.js';
|
||||
|
||||
import groups from '/user/group/user.group.permission.js';
|
||||
|
||||
import commentsMessages from '../comment/comments.messages.js';
|
||||
|
||||
import editComment from '../comment/edit/comment.edit.js';
|
||||
|
||||
import createComment from '../comment/create/comment.create.js';
|
||||
|
||||
import saveButton from './news.page.save.js';
|
||||
|
||||
import backButton from "./news.page.backButton.js"
|
||||
|
||||
import editButton from "./news.page.edit.button.js"
|
||||
|
||||
import filler from "/elements/filler.js";
|
||||
|
||||
import searchComments from "./search.comments.js";
|
||||
|
||||
import tools from "/unify/tools.js";
|
||||
|
||||
|
||||
|
||||
|
||||
class testDiv{
|
||||
|
||||
pageTitle = new newsPageTitle();
|
||||
|
||||
}
|
||||
|
||||
class testSuffixes{
|
||||
|
||||
layers = 1;
|
||||
|
||||
useCustomElement = true;
|
||||
|
||||
customElement = document.createElement("a")
|
||||
|
||||
text = "visit Unify";
|
||||
|
||||
|
||||
|
||||
visitedColor = "green";
|
||||
|
||||
linkColor = "#009688"
|
||||
|
||||
activeColor = "red"
|
||||
|
||||
|
||||
create() {
|
||||
|
||||
this.element.setAttribute("href", "https://unifyjs.org")
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default class newsPage extends news{
|
||||
|
||||
willChange = "transform";
|
||||
|
||||
transform;
|
||||
|
||||
minHeight = "100%";
|
||||
|
||||
transition = "1s"
|
||||
|
||||
|
||||
scrollbarWidth = "6px";
|
||||
|
||||
scrollbarTrackBackground = "#1c1d1e";
|
||||
|
||||
scrollbarThumbBackground = "#404040"
|
||||
|
||||
scrollbarThumbBorderRadius = "4px"
|
||||
|
||||
scrollbarThumbHoverBackground = "grey";
|
||||
|
||||
|
||||
#ifdef MACOS
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
background = "white";
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef DARK
|
||||
|
||||
background = "#282828";
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef WINDOWS
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
background = "rgb(255 255 255 / 75%)";
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef DARK
|
||||
|
||||
background = "#202020cc";
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
height = "100vh";
|
||||
|
||||
paddingTop = "";
|
||||
|
||||
boxWidth = "100vw";
|
||||
|
||||
width = "100vw";
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
background = "#f2f2f2";
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
flexDirection = "column"
|
||||
|
||||
_backButton = new backButton();
|
||||
|
||||
_testDiv = new testDiv();
|
||||
|
||||
body = new newsPageBody();
|
||||
|
||||
|
||||
editButton = new editButton();
|
||||
//testSuffixes = new testSuffixes();
|
||||
|
||||
debug = true;
|
||||
|
||||
width = 600;
|
||||
|
||||
sizing = "border-box";
|
||||
|
||||
layers = 1;
|
||||
|
||||
height = "70vh"
|
||||
|
||||
overflowY = "auto"
|
||||
|
||||
|
||||
searchComments = new searchComments();
|
||||
|
||||
commentsMessages = new commentsMessages( editComment, this.comments );
|
||||
|
||||
createComment = new createComment( this.comments );
|
||||
|
||||
filler = new filler();
|
||||
|
||||
async afterLoad() {
|
||||
|
||||
var pathName = window.location.pathname;
|
||||
|
||||
var pathParts = pathName.split("/");
|
||||
|
||||
var id = parseFloat( pathParts[1] );
|
||||
|
||||
|
||||
if( id ) {
|
||||
|
||||
this.stateMachine.composeState();
|
||||
|
||||
this.showParents();
|
||||
|
||||
this.show();
|
||||
|
||||
await this.loadPage( id )
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
state async loadPage( id ) {
|
||||
|
||||
var rightSide = this.parents("newsPages");
|
||||
|
||||
var boundBox = this.defaultElement.getBoundingClientRect();
|
||||
|
||||
var width = boundBox.width;
|
||||
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
rightSide.newsItemPage.translateX = -width;
|
||||
|
||||
#elif
|
||||
|
||||
rightSide.newsItemPage.transform = "translateX(-600px)";
|
||||
|
||||
rightSide.newsPage.transform = "translateX(-600px)";
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
this.id = id;
|
||||
|
||||
await this.sync();
|
||||
|
||||
this.createComment.create();
|
||||
|
||||
}
|
||||
|
||||
async create() {
|
||||
|
||||
await this.commentsMessages.sync();
|
||||
|
||||
}
|
||||
|
||||
permission() {
|
||||
|
||||
this.allow( groups.member, "WRITE" );
|
||||
|
||||
this.allow( groups.admin, "WRITE" );
|
||||
|
||||
this.allow( groups.visitor, "WRITE" );
|
||||
|
||||
|
||||
|
||||
this.allow( groups.member, "READ" );
|
||||
|
||||
this.allow( groups.admin, "READ" );
|
||||
|
||||
this.allow( groups.visitor, "READ" );
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
17
application/demo/page/news.page.price.js
Normal file
17
application/demo/page/news.page.price.js
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
|
||||
import newsPrice from '../news.price.js';
|
||||
|
||||
import flexbox from '/elements/flexbox.js';
|
||||
|
||||
export default class newsPagePrice extends newsPrice, flexbox{
|
||||
|
||||
useCustomElement = false;
|
||||
|
||||
fontWeight = "bold";
|
||||
|
||||
fontSize = 30;
|
||||
|
||||
padding = 20;
|
||||
|
||||
}
|
||||
16
application/demo/page/news.page.save.js
Normal file
16
application/demo/page/news.page.save.js
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
|
||||
import button from '/elements/button.js';
|
||||
|
||||
export default class saveButton extends button {
|
||||
|
||||
label = "Save";
|
||||
|
||||
async click( event, object ){
|
||||
|
||||
var result = await this.parent.save();
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
70
application/demo/page/news.page.title.js
Normal file
70
application/demo/page/news.page.title.js
Normal file
@@ -0,0 +1,70 @@
|
||||
|
||||
|
||||
import newsTitle from '../news.title.js';
|
||||
|
||||
import flexbox from '/elements/flexbox.js';
|
||||
|
||||
export default class newsPageTitle extends newsTitle, flexbox{
|
||||
|
||||
useCustomElement = false;
|
||||
|
||||
fontWeight = "bold";
|
||||
|
||||
//padding = 20;
|
||||
|
||||
//width = "600px"
|
||||
|
||||
//boxSizing = "border-box";
|
||||
|
||||
|
||||
#ifdef MACOS
|
||||
|
||||
#ifdef DARK
|
||||
|
||||
background = "#282828";
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
background = "#ffffff";
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef WINDOWS
|
||||
|
||||
#ifdef DARK
|
||||
|
||||
//background = "#202020cc";
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
//background = "rgb(255 255 255 / 75%)";
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
borderRadius = "18px 18px 0 0"
|
||||
|
||||
width = "100%"
|
||||
|
||||
#ifdef LIGHT
|
||||
|
||||
background = "white";
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
fontSize = 30;
|
||||
|
||||
padding = 20;
|
||||
|
||||
}
|
||||
28
application/demo/page/search.comments.js
Normal file
28
application/demo/page/search.comments.js
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
import input from "/elements/input.js";
|
||||
|
||||
|
||||
|
||||
|
||||
export default class searchBar extends input {
|
||||
|
||||
placeholder = "Search."
|
||||
|
||||
|
||||
async keyup( event ) {
|
||||
|
||||
this.value = this.customElement.value;
|
||||
|
||||
var value = this.value;
|
||||
|
||||
console.log("search input", value);
|
||||
|
||||
var newsItems = this.parent.commentsMessages;
|
||||
|
||||
await newsItems.search( value );
|
||||
|
||||
await newsItems.sync();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
17
application/demo/pages/appearance/panel.js
Normal file
17
application/demo/pages/appearance/panel.js
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
import os from "./rows/os.js";
|
||||
|
||||
import tint from "./rows/tint.js";
|
||||
|
||||
import panel from '/elements/panel.js';
|
||||
|
||||
|
||||
export default class appearancePanel extends panel{
|
||||
|
||||
flexDirection = "column";
|
||||
|
||||
os = new os();
|
||||
|
||||
tint = new tint();
|
||||
|
||||
}
|
||||
37
application/demo/pages/appearance/rows/os.js
Normal file
37
application/demo/pages/appearance/rows/os.js
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
import panelRow from '/elements/panel/row.js';
|
||||
|
||||
import spinner from '/elements/preloaders/simpleSpinner.js';
|
||||
|
||||
import osSelectorList from "./os.selector.list.js";
|
||||
|
||||
import osLabel from "./os.label.js";
|
||||
|
||||
|
||||
export default class os extends panelRow{
|
||||
|
||||
flexDirection = "row";
|
||||
|
||||
label = new osLabel("Os");
|
||||
|
||||
osSelector = new osSelectorList();
|
||||
|
||||
spinner = new spinner();
|
||||
|
||||
create() {
|
||||
|
||||
this.osSelector.hide();
|
||||
|
||||
}
|
||||
|
||||
afterThemeLoad() {
|
||||
|
||||
this.spinner.hide()
|
||||
|
||||
this.osSelector.show();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
8
application/demo/pages/appearance/rows/os.label.js
Normal file
8
application/demo/pages/appearance/rows/os.label.js
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
import label from '/elements/label.js';
|
||||
|
||||
export default class osLabel extends label{
|
||||
|
||||
flex = "1";
|
||||
|
||||
}
|
||||
26
application/demo/pages/appearance/rows/os.selector.js
Normal file
26
application/demo/pages/appearance/rows/os.selector.js
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
|
||||
import themeSelector from "../themeSelector.js";
|
||||
|
||||
import tools from '/unify/tools.js';
|
||||
|
||||
export default class themeOSSelectorItem extends themeSelector{
|
||||
|
||||
click() {
|
||||
|
||||
var osName = tools.CamelCase( this.selectLabel.text );
|
||||
|
||||
this.getRoot().os = osName;
|
||||
|
||||
|
||||
this.parent.updateImages( this.getRoot().tint );
|
||||
|
||||
this.parents("appearancePanel").tint.themeTintSelectors.updateImages( osName )
|
||||
|
||||
this.highlight();
|
||||
|
||||
}
|
||||
|
||||
propegateEvent = false;
|
||||
|
||||
}
|
||||
44
application/demo/pages/appearance/rows/os.selector.list.js
Normal file
44
application/demo/pages/appearance/rows/os.selector.list.js
Normal file
@@ -0,0 +1,44 @@
|
||||
|
||||
import themeOSSelector from './os.selector.js'
|
||||
|
||||
import tools from '/unify/tools.js';
|
||||
|
||||
|
||||
export default class osSelectorList{
|
||||
|
||||
themeWindows = new themeOSSelector("Windows");
|
||||
|
||||
themeMacOS = new themeOSSelector("macOS");
|
||||
|
||||
//themeAndroid = new themeOSSelector("Android");
|
||||
|
||||
updateImages( tint ) {
|
||||
|
||||
var camelCaseTint = tools.CamelCase( tint );
|
||||
|
||||
this.themeWindows.setImage("/assets/images/themeSelectors/windows" + camelCaseTint + ".png");
|
||||
|
||||
this.themeMacOS.setImage("/assets/images/themeSelectors/macos" + camelCaseTint + ".png");
|
||||
|
||||
//this.themeAndroid.setImage("/assets/images/themeSelectors/macos" + camelCaseTint + ".png");
|
||||
|
||||
}
|
||||
|
||||
create() {
|
||||
|
||||
this.themeWindows.highlight();
|
||||
|
||||
this.themeWindows.setImage('/assets/images/themeSelectors/windowsLight.png');
|
||||
|
||||
this.themeMacOS.setImage('/assets/images/themeSelectors/macosLight.png');
|
||||
|
||||
//this.themeAndroid.setImage('/assets/images/themeSelectors/macosLight.png');
|
||||
}
|
||||
|
||||
layers = 1;
|
||||
|
||||
margin = 4;
|
||||
|
||||
marginLeft = "auto";
|
||||
|
||||
}
|
||||
38
application/demo/pages/appearance/rows/tint.js
Normal file
38
application/demo/pages/appearance/rows/tint.js
Normal file
@@ -0,0 +1,38 @@
|
||||
|
||||
import panelRow from '/elements/panel/row.js';
|
||||
|
||||
import select from '/elements/selectRenderCollection.js';
|
||||
|
||||
import spinner from '/elements/preloaders/simpleSpinner.js';
|
||||
|
||||
import themeTintSelectors from "./tint.selector.list.js";
|
||||
|
||||
import customLabel from "./tint.label.js";
|
||||
|
||||
|
||||
|
||||
export default class tint extends panelRow{
|
||||
|
||||
flexDirection = "row";
|
||||
|
||||
label = new customLabel("Appearance");
|
||||
|
||||
themeTintSelectors = new themeTintSelectors();
|
||||
|
||||
spinner = new spinner();
|
||||
|
||||
create() {
|
||||
|
||||
this.themeTintSelectors.hide()
|
||||
|
||||
}
|
||||
|
||||
afterThemeLoad() {
|
||||
|
||||
this.spinner.hide()
|
||||
|
||||
this.themeTintSelectors.show();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
10
application/demo/pages/appearance/rows/tint.label.js
Normal file
10
application/demo/pages/appearance/rows/tint.label.js
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
|
||||
import label from '/elements/label.js';
|
||||
|
||||
|
||||
export default class customLabel extends label{
|
||||
|
||||
flex = "1";
|
||||
|
||||
}
|
||||
24
application/demo/pages/appearance/rows/tint.selector.js
Normal file
24
application/demo/pages/appearance/rows/tint.selector.js
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
import themeSelector from "../themeSelector.js";
|
||||
|
||||
import tools from '/unify/tools.js';
|
||||
|
||||
|
||||
export default class themeTintSelector extends themeSelector{
|
||||
|
||||
click() {
|
||||
|
||||
var tintName = tools.CamelCase( this.selectLabel.text );
|
||||
|
||||
this.parents("appearancePanel").os.osSelector.updateImages( tintName )
|
||||
|
||||
this.highlight();
|
||||
|
||||
|
||||
this.getRoot().tint = tintName;
|
||||
|
||||
}
|
||||
|
||||
propegateEvent = false;
|
||||
|
||||
}
|
||||
46
application/demo/pages/appearance/rows/tint.selector.list.js
Normal file
46
application/demo/pages/appearance/rows/tint.selector.list.js
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
import themeTintSelector from "./tint.selector.js"
|
||||
|
||||
import tools from '/unify/tools.js';
|
||||
|
||||
|
||||
export default class themeTintSelectors{
|
||||
|
||||
|
||||
themeLight = new themeTintSelector("Light");
|
||||
|
||||
themeDark = new themeTintSelector("Dark");
|
||||
|
||||
|
||||
updateImages( os ) {
|
||||
|
||||
os = os.toLowerCase();
|
||||
|
||||
var tint = tools.CamelCase( this.getRoot().tint );
|
||||
|
||||
|
||||
this.themeDark.setImage("/assets/images/themeSelectors/" + os + "Dark.png");
|
||||
|
||||
this.themeLight.setImage("/assets/images/themeSelectors/" + os + "Light.png");
|
||||
|
||||
this["theme"+tint].highlight();
|
||||
|
||||
}
|
||||
|
||||
create() {
|
||||
|
||||
this.themeDark.highlight();
|
||||
|
||||
this.themeDark.setImage('/assets/images/themeSelectors/windowsDark.png');
|
||||
|
||||
this.themeLight.setImage('/assets/images/themeSelectors/windowsLight.png');
|
||||
|
||||
}
|
||||
|
||||
layers = 1;
|
||||
|
||||
margin = 4;
|
||||
|
||||
marginLeft = "auto";
|
||||
|
||||
}
|
||||
41
application/demo/pages/appearance/themeSelector.image.js
Normal file
41
application/demo/pages/appearance/themeSelector.image.js
Normal file
@@ -0,0 +1,41 @@
|
||||
|
||||
|
||||
export default class themaSelectorImage{
|
||||
|
||||
cursor = "pointer";
|
||||
|
||||
backgroundSize = "cover";
|
||||
|
||||
borderRadius = 12;
|
||||
|
||||
layers = 1;
|
||||
|
||||
width = 80;
|
||||
|
||||
height = 80;
|
||||
|
||||
margin = 20;
|
||||
|
||||
marginBottom = 4;
|
||||
|
||||
transition = "1s"
|
||||
|
||||
|
||||
border;
|
||||
|
||||
backgroundImage;
|
||||
|
||||
|
||||
lowLight() {
|
||||
|
||||
this.border = "none";
|
||||
|
||||
}
|
||||
|
||||
highlight() {
|
||||
|
||||
this.border = "2px solid blue";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user