diff --git a/.github/badges/abap2ui5.json b/.github/badges/abap2ui5.json new file mode 100644 index 0000000..c24c4b6 --- /dev/null +++ b/.github/badges/abap2ui5.json @@ -0,0 +1,8 @@ +{ + "schemaVersion": 1, + "label": "abap2UI5", + "message": "2 apps · 3 views · 45 controls", + "color": "007ec6", + "labelColor": "555", + "cacheSeconds": 3600 +} diff --git a/.github/badges/check-abap2ui5.json b/.github/badges/check-abap2ui5.json new file mode 100644 index 0000000..687834f --- /dev/null +++ b/.github/badges/check-abap2ui5.json @@ -0,0 +1,8 @@ +{ + "schemaVersion": 1, + "label": "check-abap2UI5", + "message": "86 rules passed", + "color": "4c1", + "labelColor": "555", + "cacheSeconds": 3600 +} diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..6aad423 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,19 @@ +version: 2 + +updates: + - package-ecosystem: npm + directory: / + schedule: + interval: weekly + groups: + # one pull request for the toolchain rather than one per pin + toolchain: + patterns: ["*"] + + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + groups: + actions: + patterns: ["*"] diff --git a/.github/workflows/ABAP_CLOUD.yaml b/.github/workflows/ABAP_CLOUD.yaml deleted file mode 100644 index b5bdebd..0000000 --- a/.github/workflows/ABAP_CLOUD.yaml +++ /dev/null @@ -1,26 +0,0 @@ -name: ABAP_CLOUD - -on: - push: - branches: [main] - pull_request: - -permissions: - contents: read - -concurrency: - group: ABAP_CLOUD-${{ github.ref }} - cancel-in-progress: true - -jobs: - ABAP_CLOUD: - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - - uses: actions/checkout@v5 - - uses: actions/setup-node@v5 - with: - node-version: '22' - cache: 'npm' - - run: npm ci - - run: npx abaplint .github/abaplint/abap_cloud.jsonc diff --git a/.github/workflows/ABAP_STANDARD.yaml b/.github/workflows/ABAP_STANDARD.yaml deleted file mode 100644 index 68dec42..0000000 --- a/.github/workflows/ABAP_STANDARD.yaml +++ /dev/null @@ -1,26 +0,0 @@ -name: ABAP_STANDARD - -on: - push: - branches: [main] - pull_request: - -permissions: - contents: read - -concurrency: - group: ABAP_STANDARD-${{ github.ref }} - cancel-in-progress: true - -jobs: - ABAP_STANDARD: - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - - uses: actions/checkout@v5 - - uses: actions/setup-node@v5 - with: - node-version: '22' - cache: 'npm' - - run: npm ci - - run: npx abaplint ./abaplint.jsonc diff --git a/.github/workflows/abap-cloud.yaml b/.github/workflows/abap-cloud.yaml new file mode 100644 index 0000000..68e1638 --- /dev/null +++ b/.github/workflows/abap-cloud.yaml @@ -0,0 +1,32 @@ +name: abap-cloud + +on: + # the sibling repositories are resolved from their main branch with no + # version pin, so an upstream rename breaks this one silently. Nothing + # else notices while there is no pull request open. + schedule: + - cron: '0 5 * * 1' + workflow_dispatch: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +concurrency: + group: abap-cloud-${{ github.ref }} + cancel-in-progress: true + +jobs: + abap-cloud: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: '22' + cache: 'npm' + - run: npm ci + - run: npx abaplint .github/abaplint/abap_cloud.jsonc diff --git a/.github/workflows/abap-standard.yaml b/.github/workflows/abap-standard.yaml new file mode 100644 index 0000000..60ab654 --- /dev/null +++ b/.github/workflows/abap-standard.yaml @@ -0,0 +1,32 @@ +name: abap-standard + +on: + # the sibling repositories are resolved from their main branch with no + # version pin, so an upstream rename breaks this one silently. Nothing + # else notices while there is no pull request open. + schedule: + - cron: '0 5 * * 1' + workflow_dispatch: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +concurrency: + group: abap-standard-${{ github.ref }} + cancel-in-progress: true + +jobs: + abap-standard: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: '22' + cache: 'npm' + - run: npm ci + - run: npx abaplint ./abaplint.jsonc diff --git a/.github/workflows/build_rename.yaml b/.github/workflows/build-rename.yaml similarity index 93% rename from .github/workflows/build_rename.yaml rename to .github/workflows/build-rename.yaml index ad80cf3..2bc798e 100644 --- a/.github/workflows/build_rename.yaml +++ b/.github/workflows/build-rename.yaml @@ -1,4 +1,4 @@ -name: build_rename +name: build-rename # Renames all artifacts to a chosen namespace and pushes the result to # the branch rename_, e.g. rename_zmyui5 - ready to pull with @@ -24,11 +24,11 @@ permissions: contents: write concurrency: - group: build_rename-${{ inputs.namespace }} + group: build-rename-${{ inputs.namespace }} cancel-in-progress: false jobs: - build_rename: + build-rename: runs-on: ubuntu-latest timeout-minutes: 15 env: @@ -50,8 +50,8 @@ jobs: echo "BRANCH=rename_$ns" >> "$GITHUB_ENV" echo "Building branch: rename_$ns" - - uses: actions/checkout@v5 - - uses: actions/setup-node@v5 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: '22' cache: 'npm' diff --git a/.github/workflows/check-abap2ui5.yaml b/.github/workflows/check-abap2ui5.yaml new file mode 100644 index 0000000..8e54602 --- /dev/null +++ b/.github/workflows/check-abap2ui5.yaml @@ -0,0 +1,83 @@ +name: check-abap2ui5 + +# The abap2UI5-specific lint, as opposed to abap-standard/cloud/702, which lint +# ABAP itself. It reads a whole app class and the view it builds: the controls +# and members it writes, the structure of the document, the UI5 version floor, +# the data behind the bindings, and the abap2ui5 semantics no UI5 tooling sees. +# Settings live in abap2ui5lint.jsonc. +# +# @abap2ui5/render-runtime is a devDependency because the render gate is the +# only reason for it: it carries the OpenUI5 packages and the browser the gate +# serves. The linter names it an OPTIONAL PEER, the one dependency kind npm +# does not install by itself - without it the views stop being rendered and the +# run still passes. + +on: + pull_request: + push: + branches: [main] + # nothing else notices when an upstream rename breaks the views here + schedule: + - cron: '0 5 * * 1' + workflow_dispatch: + +permissions: + contents: write + +concurrency: + group: check-abap2ui5-${{ github.ref }} + cancel-in-progress: true + +jobs: + check-abap2ui5: + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + # the branch, not the merge commit, so the badges can be pushed back + ref: ${{ github.event.pull_request.head.ref || github.ref }} + repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }} + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: '22' + cache: 'npm' + - run: npm ci + # the render gate drives a headless browser + - run: npx playwright install chromium --with-deps + - run: npx abap2ui5lint + + # the same run once more as markdown, into the run's own page - findings + # and totals without expanding a log. --no-render because whether the views + # load was already decided above; --no-badge so a pass that skipped a gate + # cannot overwrite what the real run wrote. + - name: Job summary + if: always() + run: npx abap2ui5lint --no-render --advisory --no-progress --no-badge --format markdown >> "$GITHUB_STEP_SUMMARY" + + # The badges are committed onto the PULL REQUEST branch: main is protected + # and github-actions[bot] cannot be given a bypass, so the pull request that + # changes the views is what changes the badges, and main picks them up when + # it merges. Only after a green gate - a red pull request cannot merge, so + # its badges would never reach main, and a badge commit on top of a failing + # run reads as if something had been fixed. + - name: Publish the badges + if: success() + env: + PUSHABLE: ${{ github.event_name == 'pull_request' + && github.event.pull_request.head.repo.full_name == github.repository }} + run: | + if git diff --quiet -- .github/badges; then + echo "The linter badges are up to date." + exit 0 + fi + git diff -- .github/badges + if [ "$PUSHABLE" != "true" ]; then + echo "::warning::The linter badges are out of date and this ref cannot be pushed to. Run 'npm run check:abap2ui5' and commit .github/badges/." + exit 0 + fi + git config user.name 'github-actions[bot]' + git config user.email 'github-actions[bot]@users.noreply.github.com' + git commit -qm 'Refresh the abap2UI5-linter badges' -- .github/badges + git push + echo "Badges refreshed and pushed onto the pull request branch." diff --git a/.github/workflows/rename_test.yaml b/.github/workflows/check-rename.yaml similarity index 55% rename from .github/workflows/rename_test.yaml rename to .github/workflows/check-rename.yaml index d063063..e99c09e 100644 --- a/.github/workflows/rename_test.yaml +++ b/.github/workflows/check-rename.yaml @@ -1,4 +1,4 @@ -name: renaming +name: check-rename on: push: @@ -9,16 +9,16 @@ permissions: contents: read concurrency: - group: rename_test-${{ github.ref }} + group: check-rename-${{ github.ref }} cancel-in-progress: true jobs: - rename_test: + check-rename: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@v5 - - uses: actions/setup-node@v5 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: '22' cache: 'npm' diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..2bd5a0a --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..c49f628 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,6 @@ +# CLAUDE.md + +All project guidance lives in **[AGENTS.md](AGENTS.md)** — the single source of +truth for this repository. + +Read `AGENTS.md` before making any change. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..875a41c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,57 @@ +# Contributing + +An SQL console in the browser, without Eclipse or SAP GUI. + +## Before you open a pull request + +Run what CI runs: + +```sh +npm ci +npm run check +``` + +`npm run check` is the same set of steps the workflows run on a pull request, +so a green run locally is a green run there. `npm test` is an alias for it — +this repository has no separate unit-test suite; its ABAP is checked, not +executed. + +## What the gates are + +| Gate | What it proves | +| --- | --- | +| `npm run lint` | abaplint: syntax and style, resolved against the abap2UI5 core and the sibling addons | +| `npm run check:cloud` | the same source compiles under ABAP Cloud restrictions | +| `npm run check:abap2ui5` | [abap2UI5-linter](https://github.com/abap2UI5/linter): the app class and the view it builds, judged together — controls, members, bindings, the UI5 version floor, and a headless render of every view | +| `npm run rename` | the namespace rename still applies cleanly | + +The abap2UI5-linter keeps a baseline in `abap2ui5lint-baseline.json`. Findings +recorded there are counted and never listed; a **new** finding fails the gate, +and an entry whose finding is gone is stale and fails too. So the file only ever +shrinks — fix something, then refresh it with: + +```sh +npx abap2ui5lint --update-baseline +``` + +## Conventions + +English for code, comments, commit messages and pull requests. Commit subjects +are written in the imperative and describe the outcome, not the mechanics. One +topic per pull request. The wider rules the whole ecosystem follows live in +[abap2UI5's CONVENTIONS.md](https://github.com/abap2UI5/abap2UI5/blob/main/.github/shared/CONVENTIONS.md). + +## Views + +Views are built with `z2ui5_cl_ui5_view_builder`, the generic builder in the +core's `src/02` — five verbs (`ele`, `tag`, `a`, `end`, `stringify`) that +translate a UI5 XML view one to one. Two things to know before editing a chain: + +- `a( )` applies to the element the chain is pointing at, so give a control its + attributes immediately after the `ele( )` or `tag( )` that opened it; +- an ABAP boolean goes into `b =`, never `v =`. Through `v =` an `abap_false` + renders as an empty string, which UI5 reads as true. + +The frozen `z2ui5_cl_xml_view` is gone from this repository. Do not reintroduce +it: it lives in the core's `src/99`, outside the released API, and the view gate +can read nothing that is built with it. diff --git a/README.md b/README.md index 9640686..b2c3d78 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,15 @@ -[![ABAP_STANDARD](https://github.com/abap2UI5-addons/sql-console/actions/workflows/ABAP_STANDARD.yaml/badge.svg)](https://github.com/abap2UI5-addons/sql-console/actions/workflows/ABAP_STANDARD.yaml) -[![ABAP_CLOUD](https://github.com/abap2UI5-addons/sql-console/actions/workflows/ABAP_CLOUD.yaml/badge.svg)](https://github.com/abap2UI5-addons/sql-console/actions/workflows/ABAP_CLOUD.yaml) -[![ABAP_702](https://github.com/abap2UI5-addons/sql-console/actions/workflows/ABAP_702.yaml/badge.svg)](https://github.com/abap2UI5-addons/sql-console/actions/workflows/ABAP_702.yaml) +![ABAP](https://img.shields.io/badge/ABAP-Standard%20%E2%86%92%20Cloud-blue) +[![namespace](https://img.shields.io/badge/namespace-z2ui5__sql__cl-blue)](abaplint.jsonc) +[![dependency](https://img.shields.io/badge/dependency-abap2UI5-blue)](https://github.com/abap2UI5/abap2UI5) +[![abap2UI5](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fabap2UI5-addons%2Fsql-console%2Fmain%2F.github%2Fbadges%2Fabap2ui5.json)](https://github.com/abap2UI5-addons/sql-console/actions/workflows/check-abap2ui5.yaml) +

+[![abap-standard](https://github.com/abap2UI5-addons/sql-console/actions/workflows/abap-standard.yaml/badge.svg)](https://github.com/abap2UI5-addons/sql-console/actions/workflows/abap-standard.yaml) +[![abap-cloud](https://github.com/abap2UI5-addons/sql-console/actions/workflows/abap-cloud.yaml/badge.svg)](https://github.com/abap2UI5-addons/sql-console/actions/workflows/abap-cloud.yaml)
-[![auto_cloud](https://github.com/abap2UI5-addons/sql-console/actions/workflows/auto_cloud.yaml/badge.svg)](https://github.com/abap2UI5-addons/sql-console/actions/workflows/auto_cloud.yaml) -[![auto_downport](https://github.com/abap2UI5-addons/sql-console/actions/workflows/auto_downport.yaml/badge.svg)](https://github.com/abap2UI5-addons/sql-console/actions/workflows/auto_downport.yaml) +[![check-abap2UI5](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fabap2UI5-addons%2Fsql-console%2Fmain%2F.github%2Fbadges%2Fcheck-abap2ui5.json)](https://github.com/abap2UI5-addons/sql-console/actions/workflows/check-abap2ui5.yaml) +[![check-rename](https://github.com/abap2UI5-addons/sql-console/actions/workflows/check-rename.yaml/badge.svg)](https://github.com/abap2UI5-addons/sql-console/actions/workflows/check-rename.yaml)
-[![renaming](https://github.com/abap2UI5-addons/sql-console/actions/workflows/rename_test.yaml/badge.svg)](https://github.com/abap2UI5-addons/sql-console/actions/workflows/rename_test.yaml) +[![build-rename](https://github.com/abap2UI5-addons/sql-console/actions/workflows/build-rename.yaml/badge.svg)](https://github.com/abap2UI5-addons/sql-console/actions/workflows/build-rename.yaml) # sql-console SQL Console in Your Browser – No Need for Eclipse or SAP GUI Installation diff --git a/abap2ui5lint-baseline.json b/abap2ui5lint-baseline.json new file mode 100644 index 0000000..8a005fd --- /dev/null +++ b/abap2ui5lint-baseline.json @@ -0,0 +1,16 @@ +{ + "note": "abap2ui5-linter baseline: findings that existed when the linter was adopted. Suppressed on every run; NEW findings still fail, a STALE entry fails too. Regenerate with --update-baseline.", + "findings": { + "src/abap/z2ui5_sql_cl_app_01.clas.abap|non-released-api||src/00/01|z2ui5_cl_ajson_mapping": 1, + "src/abap/z2ui5_sql_cl_app_01.clas.abap|non-released-api||src/99/01|z2ui5_cl_util": 1, + "src/abap/z2ui5_sql_cl_app_01.clas.abap|non-released-api||src/99/01|z2ui5_cx_util_error": 1, + "src/abap/z2ui5_sql_cl_app_01.clas.abap|obsolete-binder||_bind_edit|": 1, + "src/abap/z2ui5_sql_cl_app_01.clas.abap|obsolete-model-update||view_model_update|": 1, + "src/abap/z2ui5_sql_cl_app_01.clas.abap|unused-public-attribute||mv_column_config|": 1, + "src/abap/z2ui5_sql_cl_app_01.clas.abap|unused-public-attribute||t_tab_filter|": 1, + "src/abap/z2ui5_sql_cl_app_01.clas.abap|unused-public-attribute||t_tab_group|": 1, + "src/abap/z2ui5_sql_cl_app_01.clas.abap|unused-public-attribute||t_tab_sort|": 1, + "src/abap/z2ui5_sql_cl_app_01.clas.abap|unused-public-attribute||t_tab|": 1, + "src/native/zcl_2ui5_native_sql_console.clas.abap|unused-public-attribute||mr_output_data|": 1 + } +} diff --git a/abap2ui5lint.jsonc b/abap2ui5lint.jsonc new file mode 100644 index 0000000..624d034 --- /dev/null +++ b/abap2ui5lint.jsonc @@ -0,0 +1,42 @@ +{ + // abap2UI5-linter - checks a whole app class without an SAP system: the ABAP + // and the view it produces, together. Its last rule group is about the + // relationship between the two, which is where the defects live that stay + // silent at runtime and that no UI5 tooling can see. + // https://github.com/abap2UI5/linter + "$schema": "./node_modules/@abap2ui5/linter/data/abap2ui5lint.schema.json", + + "paths": ["src"], + + // The oldest release this addon promises to run on. Raise it only when the + // code genuinely needs a newer member: a floor above what is used stops the + // gate reporting members that are missing on the systems people run. + "ui5": "1.71", + "distribution": "openui5", + + // Required, not merely left on. It is on by default, and a default-on gate + // whose runtime is missing steps aside and the run stays green - which for + // views nobody can otherwise open would be the wrong kind of quiet. + "render": true, + + // Adoption-time debt. These views only became readable when the repository + // moved onto z2ui5_cl_ui5_view_builder; before that the linter found nothing + // to check here at all, so the whole backlog arrived in one run. The + // baseline freezes exactly what existed at that moment: those findings are + // counted and never listed, a NEW one fails normally, and an entry whose + // finding is gone is stale and fails too - so this file only shrinks. + // Refresh it with `npx abap2ui5lint --update-baseline` after fixing things. + "baseline": "abap2ui5lint-baseline.json", + + // The two README badges, rewritten on every run. They say two different + // things: what is here, and what CI made of it. A green workflow badge only + // means the job exited zero - which was also true while the linter could not + // read a single view in this repository. + "badge": [ + { "kind": "corpus", "file": ".github/badges/abap2ui5.json" }, + { "kind": "checks", "file": ".github/badges/check-abap2ui5.json" } + ], + + + "failOn": "warning" +} diff --git a/package-lock.json b/package-lock.json index 7e1cd73..b4f9b08 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,12 +9,62 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { + "@abap2ui5/linter": "^0.2.1", + "@abap2ui5/render-runtime": "^0.1.1", "@abaplint/cli": "^2.119.66" }, "engines": { "node": ">=22" } }, + "node_modules/@abap2ui5/linter": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@abap2ui5/linter/-/linter-0.2.2.tgz", + "integrity": "sha512-WegxIRN5M5gH13Ye5Mo13fbpjAZ183J0ei1iArCfEiKUPAGqHot20NnckpNnXHX+YPHCkehA+Z0QapXjaLK1Tw==", + "dev": true, + "license": "MIT", + "workspaces": [ + "render-runtime" + ], + "bin": { + "abap2ui5lint": "cli.mjs" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "@abap2ui5/render-runtime": "^0.1.0 || ^0.2.0" + }, + "peerDependenciesMeta": { + "@abap2ui5/render-runtime": { + "optional": true + } + } + }, + "node_modules/@abap2ui5/render-runtime": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@abap2ui5/render-runtime/-/render-runtime-0.1.1.tgz", + "integrity": "sha512-41O4amDSbHWynzXiTjWYp1XC+dVbWc58flTkTTCJUjXwYXW6mJBmMiLxBSpCz1pMXa4LL3QAZq8IXk7Yy2664g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@openui5/sap.f": "1.151.0", + "@openui5/sap.m": "1.151.0", + "@openui5/sap.tnt": "1.151.0", + "@openui5/sap.ui.codeeditor": "1.151.0", + "@openui5/sap.ui.core": "1.151.0", + "@openui5/sap.ui.integration": "1.151.0", + "@openui5/sap.ui.layout": "1.151.0", + "@openui5/sap.ui.table": "1.151.0", + "@openui5/sap.ui.unified": "1.151.0", + "@openui5/sap.uxap": "1.151.0", + "@openui5/themelib_sap_horizon": "1.151.0", + "playwright": "^1.61.1" + }, + "engines": { + "node": ">=22" + } + }, "node_modules/@abaplint/cli": { "version": "2.119.66", "resolved": "https://registry.npmjs.org/@abaplint/cli/-/cli-2.119.66.tgz", @@ -30,6 +80,170 @@ "funding": { "url": "https://github.com/sponsors/larshp" } + }, + "node_modules/@openui5/sap.f": { + "version": "1.151.0", + "resolved": "https://registry.npmjs.org/@openui5/sap.f/-/sap.f-1.151.0.tgz", + "integrity": "sha512-nSNAM+/Gqniqk53rakq0heM2dJ0I14ert6s3sA93DjHBZpQOcaAchfY5idzI2OSU3G7OHtbOZKCrfwSZ2eSVZQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@openui5/sap.m": "1.151.0", + "@openui5/sap.ui.core": "1.151.0", + "@openui5/sap.ui.layout": "1.151.0" + } + }, + "node_modules/@openui5/sap.m": { + "version": "1.151.0", + "resolved": "https://registry.npmjs.org/@openui5/sap.m/-/sap.m-1.151.0.tgz", + "integrity": "sha512-CzGB4/ckoCHrlpSvsah2seGYvb/nOqoWqYtKHI0TncGaQ/bKq0omqsa3Z0rGApqXWv/wWS6O1EYBgDlv5NWFPQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@openui5/sap.ui.core": "1.151.0", + "@openui5/sap.ui.layout": "1.151.0", + "@openui5/sap.ui.unified": "1.151.0" + } + }, + "node_modules/@openui5/sap.tnt": { + "version": "1.151.0", + "resolved": "https://registry.npmjs.org/@openui5/sap.tnt/-/sap.tnt-1.151.0.tgz", + "integrity": "sha512-BEV7fShRmvq+mroDZASzT+jYyWPxNQI1k0y1ZXG/fYUhQ1pJYSphm1ZL0b+QCcc13o+m5jGQyy2WNJdiFJPnLw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@openui5/sap.m": "1.151.0", + "@openui5/sap.ui.core": "1.151.0" + } + }, + "node_modules/@openui5/sap.ui.codeeditor": { + "version": "1.151.0", + "resolved": "https://registry.npmjs.org/@openui5/sap.ui.codeeditor/-/sap.ui.codeeditor-1.151.0.tgz", + "integrity": "sha512-rQ0Hdv+0HJLHrPjXMV1MC13QTa7z0OsG9nwxY5Rdk4GuFIfd0QM1IcQISFg77Jl+ng+SIJS09frIjz4oO/a8/g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@openui5/sap.ui.core": "1.151.0" + } + }, + "node_modules/@openui5/sap.ui.core": { + "version": "1.151.0", + "resolved": "https://registry.npmjs.org/@openui5/sap.ui.core/-/sap.ui.core-1.151.0.tgz", + "integrity": "sha512-0cIWVWZ4mmsAl+b+HKiK50mY0Pq4mBE10wC79xIwvK904f7yTUQW+TIqAmYIsXm0TrcxCqGY7DvfHnrVJTao5Q==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@openui5/sap.ui.integration": { + "version": "1.151.0", + "resolved": "https://registry.npmjs.org/@openui5/sap.ui.integration/-/sap.ui.integration-1.151.0.tgz", + "integrity": "sha512-dOUbakjdziJRIE+jy2S363A5W+1BqoPWJwbJ9IQDHq6VCoNDTYCaol+YXN2mpUH5IZfYCNMaW8OpBdDaZ7kBng==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@openui5/sap.f": "1.151.0", + "@openui5/sap.m": "1.151.0", + "@openui5/sap.ui.core": "1.151.0", + "@openui5/sap.ui.layout": "1.151.0", + "@openui5/sap.ui.unified": "1.151.0" + } + }, + "node_modules/@openui5/sap.ui.layout": { + "version": "1.151.0", + "resolved": "https://registry.npmjs.org/@openui5/sap.ui.layout/-/sap.ui.layout-1.151.0.tgz", + "integrity": "sha512-TNhwtKkZLDcSfhxa5p6dpw7NvJ0qb9K70lq0fa15ahTi/RQKE3AAH4lz8Y40UwtJwtRGZN6TPz/hhBrfCvqC0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@openui5/sap.ui.core": "1.151.0" + } + }, + "node_modules/@openui5/sap.ui.table": { + "version": "1.151.0", + "resolved": "https://registry.npmjs.org/@openui5/sap.ui.table/-/sap.ui.table-1.151.0.tgz", + "integrity": "sha512-TmSLiL/FclgO1k0cjMbBn1zYW1D4eE1Nw3D0gcCeWBX+Ph080TWaPkULBAyO+ZLrsFD/YPeDIqHru92JJbOdGA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@openui5/sap.ui.core": "1.151.0", + "@openui5/sap.ui.unified": "1.151.0" + } + }, + "node_modules/@openui5/sap.ui.unified": { + "version": "1.151.0", + "resolved": "https://registry.npmjs.org/@openui5/sap.ui.unified/-/sap.ui.unified-1.151.0.tgz", + "integrity": "sha512-GPnsaXfLwXfgBt0pT1WvbXXZL5xwCz2D2BcFbupHn51abz9FGZsiroASnbZLuP3XFoxXGBJBbNLAbAoWOfX+bQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@openui5/sap.ui.core": "1.151.0" + } + }, + "node_modules/@openui5/sap.uxap": { + "version": "1.151.0", + "resolved": "https://registry.npmjs.org/@openui5/sap.uxap/-/sap.uxap-1.151.0.tgz", + "integrity": "sha512-VqdvIaSbGX3Ex7hI3GuTrH33OmWiBhg8/HECNTVSmsVQTwesRYR6lPjZANzKmwPd5k1tL0JanXgSmgt7Akn2yA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@openui5/sap.f": "1.151.0", + "@openui5/sap.m": "1.151.0", + "@openui5/sap.ui.core": "1.151.0", + "@openui5/sap.ui.layout": "1.151.0" + } + }, + "node_modules/@openui5/themelib_sap_horizon": { + "version": "1.151.0", + "resolved": "https://registry.npmjs.org/@openui5/themelib_sap_horizon/-/themelib_sap_horizon-1.151.0.tgz", + "integrity": "sha512-kyNiL/WlEQV1WrYneI8KZBpPmHzwpaPVLn0kqOS61XUgWz0z97ESmarAT3JuX7gzNAx4vxEaV5bXSYZ1VDxWng==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/playwright": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.62.1.tgz", + "integrity": "sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.62.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.62.1.tgz", + "integrity": "sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=20" + } } } } diff --git a/package.json b/package.json index 26a49fc..73fcb0a 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,12 @@ "version": "1.0.0", "description": "SQL Console in Your Browser, built with abap2UI5.", "scripts": { - "rename": "abaplint .github/abaplint/rename.json --rename" + "rename": "abaplint .github/abaplint/rename.json --rename", + "lint": "abaplint ./abaplint.jsonc", + "check:cloud": "abaplint .github/abaplint/abap_cloud.jsonc", + "check:abap2ui5": "abap2ui5lint", + "check": "npm run lint && npm run check:cloud && npm run check:abap2ui5 && npm run rename", + "test": "npm run check" }, "repository": { "type": "git", @@ -16,7 +21,9 @@ }, "homepage": "https://github.com/abap2UI5-addons/sql-console#readme", "devDependencies": { - "@abaplint/cli": "^2.119.66" + "@abaplint/cli": "^2.119.66", + "@abap2ui5/linter": "^0.2.1", + "@abap2ui5/render-runtime": "^0.1.1" }, "engines": { "node": ">=22" diff --git a/src/abap/z2ui5_sql_cl_app_01.clas.abap b/src/abap/z2ui5_sql_cl_app_01.clas.abap index 7b60bc2..7b824cc 100644 --- a/src/abap/z2ui5_sql_cl_app_01.clas.abap +++ b/src/abap/z2ui5_sql_cl_app_01.clas.abap @@ -116,11 +116,11 @@ CLASS z2ui5_sql_cl_app_01 DEFINITION PUBLIC. METHODS sql_view_display IMPORTING - view_sql TYPE REF TO z2ui5_cl_xml_view. + view_sql TYPE REF TO z2ui5_cl_ui5_view_builder. METHODS preview_view. METHODS history_view IMPORTING - view_history TYPE REF TO z2ui5_cl_xml_view. + view_history TYPE REF TO z2ui5_cl_ui5_view_builder. METHODS preview_on_filter_clear. METHODS z2ui5_on_callback_pop_confirm IMPORTING @@ -261,25 +261,32 @@ CLASS z2ui5_sql_cl_app_01 IMPLEMENTATION. METHOD history_view. - view_history->list( - items = client->_bind_edit( ms_draft-history_tab ) - mode = `SingleSelectMaster` - selectionchange = client->_event( val = `HISTORY_LOAD` ) - sticky = `ColumnHeaders,HeaderToolbar` - )->header_toolbar( - )->overflow_toolbar( - )->title( `Query History` - )->toolbar_spacer( - )->toolbar_spacer( - )->button( text = `New` press = client->_event( `HISTORY_CREATE` ) icon = `sap-icon://create` - )->button( text = `Clear` press = client->_event( `HISTORY_CLEAR` ) icon = `sap-icon://delete` - )->get_parent( )->get_parent( - )->standard_list_item( - title = `{S_DB/TABNAME} - {DATE} {TIME}` - description = `{S_DB/SQL_COMMAND}` - info = `{S_DB/COUNTER}` - selected = `{SELKZ}` - ). + view_history->ele( `List` + )->a( n = `items` v = client->_bind_edit( ms_draft-history_tab ) + )->a( n = `mode` v = `SingleSelectMaster` + )->a( n = `selectionChange` v = client->_event( val = `HISTORY_LOAD` ) + )->a( n = `sticky` v = `ColumnHeaders,HeaderToolbar` + )->ele( `headerToolbar` + )->ele( `OverflowToolbar` + )->tag( `Title` + )->a( n = `text` v = `Query History` + )->tag( `ToolbarSpacer` + )->tag( `ToolbarSpacer` + )->tag( `Button` + )->a( n = `text` v = `New` + )->a( n = `press` v = client->_event( `HISTORY_CREATE` ) + )->a( n = `icon` v = `sap-icon://create` + )->tag( `Button` + )->a( n = `text` v = `Clear` + )->a( n = `press` v = client->_event( `HISTORY_CLEAR` ) + )->a( n = `icon` v = `sap-icon://delete` + )->end( + )->end( + )->tag( `StandardListItem` + )->a( n = `title` v = `{S_DB/TABNAME} - {DATE} {TIME}` + )->a( n = `description` v = `{S_DB/SQL_COMMAND}` + )->a( n = `info` v = `{S_DB/COUNTER}` + )->a( n = `selected` v = `{SELKZ}` ). ENDMETHOD. @@ -354,48 +361,80 @@ CLASS z2ui5_sql_cl_app_01 IMPLEMENTATION. METHOD preview_view. - DATA(lo_view_nested) = z2ui5_cl_xml_view=>factory( ). + DATA(lo_view_nested) = z2ui5_cl_ui5_view_builder=>factory( + )->ele( n = `View` ns = `mvc` + )->a( n = `xmlns` v = `sap.m` + )->a( n = `xmlns:mvc` v = `sap.ui.core.mvc` + )->a( n = `xmlns:core` v = `sap.ui.core` + )->a( n = `xmlns:editor` v = `sap.ui.codeeditor` + )->a( n = `xmlns:html` v = `http://www.w3.org/1999/xhtml` + )->a( n = `xmlns:layout` v = `sap.ui.layout` + )->a( n = `xmlns:table` v = `sap.ui.table` + )->a( n = `xmlns:z2ui5` v = `z2ui5.cc` + )->a( n = `xmlns:z2ui5_cci` v = `z2ui5_cci.cc` + )->a( n = `displayBlock` v = `true` + )->a( n = `height` v = `100%` ). IF ms_draft-s_preview-tab IS BOUND. FIELD-SYMBOLS TYPE table. mr_preview_tab = ms_draft-s_preview-tab. ASSIGN mr_preview_tab->* TO . - DATA(tab) = lo_view_nested->ui_table( - id = `previewTab` - rows = client->_bind( ) - editable = abap_false - alternaterowcolors = abap_true - showcolumnvisibilitymenu = abap_true - enableselectall = abap_false - selectionbehavior = `RowOnly` - visiblerowcountmode = `Interactive` - visiblerowcount = `7` - selectionmode = `None` ). - tab->ui_extension( )->overflow_toolbar( width = `100%` - )->title( client->_bind( ms_draft-s_preview-title ) - )->toolbar_spacer( - )->input( width = `30%` value = client->_bind_edit( ms_draft-s_preview-search_field ) description = `All Column Search` - submit = client->_event( `PREVIEW_SEARCH` ) - )->toolbar_spacer( - )->_z2ui5( )->spreadsheet_export( tableid = `previewTab` icon = `sap-icon://excel-attachment` type = `Emphasized` - columnconfig = client->_bind( val = mt_column_config - custom_filter = NEW z2ui5_cl_cc_spreadsheet( ) - custom_mapper = z2ui5_cl_ajson_mapping=>create_lower_case( ) - ) - )->get_parent( - ). + DATA(tab) = lo_view_nested->ele( n = `Table` ns = `table` + )->a( n = `id` v = `previewTab` + )->a( n = `rows` v = client->_bind( ) + )->a( n = `editable` b = abap_false + )->a( n = `alternateRowColors` b = abap_true + )->a( n = `showColumnVisibilityMenu` b = abap_true + )->a( n = `enableSelectAll` b = abap_false + )->a( n = `selectionBehavior` v = `RowOnly` + )->a( n = `visibleRowCountMode` v = `Interactive` + )->a( n = `visibleRowCount` v = `7` + )->a( n = `selectionMode` v = `None` ). + DATA(toolbar) = tab->ele( n = `extension` ns = `table` + )->ele( `OverflowToolbar` + )->a( n = `width` v = `100%` + )->tag( `Title` + )->a( n = `text` v = client->_bind( ms_draft-s_preview-title ) + )->tag( `ToolbarSpacer` + )->tag( `Input` + )->a( n = `width` v = `30%` + )->a( n = `value` v = client->_bind_edit( ms_draft-s_preview-search_field ) + )->a( n = `description` v = `All Column Search` + )->a( n = `submit` v = client->_event( `PREVIEW_SEARCH` ) + )->tag( `ToolbarSpacer` ). + + " the control's XML element belongs to custom-controls, which delivers + " the matching JavaScript as a BSP - building the tag here by hand is + " what let the two drift apart + z2ui5_cl_cci_spreadsheet=>render( + view = toolbar + tableid = `previewTab` + icon = `sap-icon://excel-attachment` + type = `Emphasized` + columns = client->_bind( val = mt_column_config + custom_filter = NEW z2ui5_cl_cci_json_filter( ) + custom_mapper = z2ui5_cl_ajson_mapping=>create_lower_case( ) ) ). DATA(lt_fields) = z2ui5_cl_util=>rtti_get_t_attri_by_any( ). - DATA(lo_columns) = tab->ui_columns( ). + DATA(lo_columns) = tab->ele( n = `columns` ns = `table` ). LOOP AT lt_fields INTO DATA(lv_field). - lo_columns->ui_column( width = `auto` sortproperty = `'` && lv_field-name && `'` filterproperty = `'` && lv_field-name && `'` - )->text( text = lv_field-name )->ui_template( )->label( text = `{` && lv_field-name && `}` wrapping = abap_true ). + lo_columns->ele( n = `Column` ns = `table` + )->a( n = `width` v = `auto` + )->a( n = `sortProperty` v = `'` && lv_field-name && `'` + )->a( n = `filterProperty` v = `'` && lv_field-name && `'` + )->tag( `Text` + )->a( n = `text` v = lv_field-name + )->ele( n = `template` ns = `table` + )->tag( `Label` + )->a( n = `text` v = `{` && lv_field-name && `}` + )->a( n = `wrapping` b = abap_true ). ENDLOOP. ELSE. - lo_view_nested->text( `Data preview...` ). + lo_view_nested->tag( `Text` + )->a( n = `text` v = `Data preview...` ). ENDIF. client->nest_view_display( val = lo_view_nested->stringify( ) id = `preview` method_insert = `addItem` ). @@ -486,10 +525,13 @@ CLASS z2ui5_sql_cl_app_01 IMPLEMENTATION. METHOD sql_view_display. - view_sql->button( text = `Filter` press = client->_event( `PREVIEW_FILTER` ) icon = `sap-icon://filter` - )->code_editor( - type = `sql` - value = client->_bind_edit( ms_draft-sql_input ) ). + view_sql->tag( `Button` + )->a( n = `text` v = `Filter` + )->a( n = `press` v = client->_event( `PREVIEW_FILTER` ) + )->a( n = `icon` v = `sap-icon://filter` + )->tag( n = `CodeEditor` ns = `editor` + )->a( n = `type` v = `sql` + )->a( n = `value` v = client->_bind_edit( ms_draft-sql_input ) ). ENDMETHOD. @@ -501,10 +543,17 @@ CLASS z2ui5_sql_cl_app_01 IMPLEMENTATION. IF client->check_on_init( ). - client->view_display( z2ui5_cl_xml_view=>factory( - )->_z2ui5( )->timer( client->_event( `START` ) - )->_generic( ns = `html` name = `script` )->_cc_plain_xml( z2ui5_cl_cc_spreadsheet=>get_js( ) - )->stringify( ) ). + client->view_display( z2ui5_cl_ui5_view_builder=>factory( + )->ele( n = `View` ns = `mvc` + )->a( n = `xmlns` v = `sap.m` + )->a( n = `xmlns:mvc` v = `sap.ui.core.mvc` + )->a( n = `xmlns:core` v = `sap.ui.core` + )->a( n = `xmlns:z2ui5` v = `z2ui5.cc` + )->a( n = `displayBlock` v = `true` + )->a( n = `height` v = `100%` + )->tag( n = `Timer` ns = `z2ui5` + )->a( n = `finished` v = client->_event( `START` ) + )->stringify( ) ). RETURN. ENDIF. @@ -630,51 +679,88 @@ CLASS z2ui5_sql_cl_app_01 IMPLEMENTATION. METHOD z2ui5_view_display. - DATA(view) = z2ui5_cl_xml_view=>factory( ). - DATA(page) = view->shell( appwidthlimited = client->_bind_edit( ms_draft-appwidthlimited ) )->page( - title = `ABAP SQL Console` - navbuttonpress = client->_event( `BACK` ) - shownavbutton = abap_true - )->header_content( - )->overflow_toolbar( - )->label( `Max Rows` - )->input( width = `15%` value = client->_bind_edit( ms_draft-sql_max_rows ) - )->button( - text = `Run` - press = client->_event( `RUN` ) - type = `Emphasized` - )->toolbar_spacer( - )->label( `Shell` - )->switch( state = client->_bind_edit( ms_draft-appwidthlimited ) - )->link( text = `Project on GitHub` target = `_blank` href = `https://github.com/abap2UI5-addons/sql-console` - )->get_parent( )->get_parent( ). - - page->grid( `L7 M12 S12` )->content( `layout` ). - - DATA(cont_main) = page->responsive_splitter( defaultpane = `default` - )->pane_container( orientation = `Vertical` ). - - DATA(cont_sub) = cont_main->pane_container( orientation = `Horizontal` ). - - DATA(view_sql) = cont_sub->split_pane( requiredparentwidth = `600` - )->layout_data( ns = `layout` - )->splitter_layout_data( size = client->_bind_edit( ms_draft-sql_cont_size ) - )->get_parent( )->get_parent( ). + DATA(view) = z2ui5_cl_ui5_view_builder=>factory( + )->ele( n = `View` ns = `mvc` + )->a( n = `xmlns` v = `sap.m` + )->a( n = `xmlns:mvc` v = `sap.ui.core.mvc` + )->a( n = `xmlns:core` v = `sap.ui.core` + )->a( n = `xmlns:editor` v = `sap.ui.codeeditor` + )->a( n = `xmlns:html` v = `http://www.w3.org/1999/xhtml` + )->a( n = `xmlns:layout` v = `sap.ui.layout` + )->a( n = `xmlns:table` v = `sap.ui.table` + )->a( n = `xmlns:z2ui5` v = `z2ui5.cc` + )->a( n = `displayBlock` v = `true` + )->a( n = `height` v = `100%` ). + DATA(page) = view->ele( `Shell` + )->a( n = `appWidthLimited` v = client->_bind_edit( ms_draft-appwidthlimited ) + )->ele( `Page` + )->a( n = `title` v = `ABAP SQL Console` + )->a( n = `navButtonPress` v = client->_event( `BACK` ) + )->a( n = `showNavButton` b = abap_true + )->ele( `headerContent` + )->ele( `OverflowToolbar` + )->tag( `Label` + )->a( n = `text` v = `Max Rows` + )->tag( `Input` + )->a( n = `width` v = `15%` + )->a( n = `value` v = client->_bind_edit( ms_draft-sql_max_rows ) + )->tag( `Button` + )->a( n = `text` v = `Run` + )->a( n = `press` v = client->_event( `RUN` ) + )->a( n = `type` v = `Emphasized` + )->tag( `ToolbarSpacer` + )->tag( `Label` + )->a( n = `text` v = `Shell` + )->tag( `Switch` + )->a( n = `state` v = client->_bind_edit( ms_draft-appwidthlimited ) + )->tag( `Link` + )->a( n = `text` v = `Project on GitHub` + )->a( n = `target` v = `_blank` + )->a( n = `href` v = `https://github.com/abap2UI5-addons/sql-console` + )->end( + )->end( ). + + page->ele( n = `Grid` ns = `layout` + )->a( n = `defaultSpan` v = `L7 M12 S12` + )->ele( n = `content` ns = `layout` ). + + DATA(cont_main) = page->ele( n = `ResponsiveSplitter` ns = `layout` + )->a( n = `defaultPane` v = `default` + )->ele( n = `PaneContainer` ns = `layout` + )->a( n = `orientation` v = `Vertical` ). + + DATA(cont_sub) = cont_main->ele( n = `PaneContainer` ns = `layout` + )->a( n = `orientation` v = `Horizontal` ). + + DATA(view_sql) = cont_sub->ele( n = `SplitPane` ns = `layout` + )->a( n = `requiredParentWidth` v = `600` + )->ele( n = `layoutData` ns = `layout` + )->ele( n = `SplitterLayoutData` ns = `layout` + )->a( n = `size` v = client->_bind_edit( ms_draft-sql_cont_size ) + )->end( + )->end( ). sql_view_display( view_sql ). - DATA(view_history) = cont_sub->split_pane( requiredparentwidth = `400` - )->layout_data( ns = `layout` - )->splitter_layout_data( size = client->_bind_edit( ms_draft-history_cont_size ) - )->get_parent( )->get_parent( ). + DATA(view_history) = cont_sub->ele( n = `SplitPane` ns = `layout` + )->a( n = `requiredParentWidth` v = `400` + )->ele( n = `layoutData` ns = `layout` + )->ele( n = `SplitterLayoutData` ns = `layout` + )->a( n = `size` v = client->_bind_edit( ms_draft-history_cont_size ) + )->end( + )->end( ). history_view( view_history ). - cont_main->split_pane( requiredparentwidth = `400` - )->layout_data( ns = `layout` - )->splitter_layout_data( size = client->_bind_edit( ms_draft-s_preview-cont_size ) - )->get_parent( )->get_parent( - )->vbox( id = `preview` ). + cont_main->ele( n = `SplitPane` ns = `layout` + )->a( n = `requiredParentWidth` v = `400` + )->ele( n = `layoutData` ns = `layout` + )->ele( n = `SplitterLayoutData` ns = `layout` + )->a( n = `size` v = client->_bind_edit( ms_draft-s_preview-cont_size ) + )->end( + )->end( + )->ele( `VBox` + )->a( n = `id` v = `preview` ). preview_view( ). diff --git a/src/native/zcl_2ui5_native_sql_console.clas.abap b/src/native/zcl_2ui5_native_sql_console.clas.abap index b420c8d..73b7bff 100644 --- a/src/native/zcl_2ui5_native_sql_console.clas.abap +++ b/src/native/zcl_2ui5_native_sql_console.clas.abap @@ -94,11 +94,20 @@ class zcl_2ui5_native_sql_console implementation. importing dbuser = standard_connection_schema. - me->a_ui5_client->view_display( z2ui5_cl_xml_view=>factory( )->_z2ui5( )->timer( me->a_ui5_client->_event( on_start=>event_name( ) ) - )->_generic( ns = `html` ##NO_TEXT - name = `script` - )->_cc_plain_xml( z2ui5_cl_cc_spreadsheet=>get_js( ) - )->stringify( ) ). + " the exporter's JavaScript used to be injected into an html:script tag + " here; custom-controls now ships it as a BSP, so the view only needs the + " timer that kicks the app off + me->a_ui5_client->view_display( z2ui5_cl_ui5_view_builder=>factory( + )->ele( n = `View` ns = `mvc` + )->a( n = `xmlns` v = `sap.m` + )->a( n = `xmlns:mvc` v = `sap.ui.core.mvc` + )->a( n = `xmlns:core` v = `sap.ui.core` + )->a( n = `xmlns:z2ui5` v = `z2ui5.cc` + )->a( n = `displayBlock` v = `true` + )->a( n = `height` v = `100%` + )->tag( n = `Timer` ns = `z2ui5` + )->a( n = `finished` v = me->a_ui5_client->_event( on_start=>event_name( ) ) + )->stringify( ) ). me->state->page = value #( app_width_limited = abap_true ). diff --git a/src/native/zcl_2ui5_native_sql_console.clas.locals_imp.abap b/src/native/zcl_2ui5_native_sql_console.clas.locals_imp.abap index ca262d7..75483ad 100644 --- a/src/native/zcl_2ui5_native_sql_console.clas.locals_imp.abap +++ b/src/native/zcl_2ui5_native_sql_console.clas.locals_imp.abap @@ -91,7 +91,7 @@ class main_view definition data a_ui5_client type ref to z2ui5_if_client. - data a_parser type ref to z2ui5_cl_xml_view. + data a_parser type ref to z2ui5_cl_ui5_view_builder. endclass. class data_result_view definition @@ -116,7 +116,7 @@ class data_result_view definition data a_ui5_client type ref to z2ui5_if_client. - data a_parser type ref to z2ui5_cl_xml_view. + data a_parser type ref to z2ui5_cl_ui5_view_builder. endclass. interface ui_interaction. @@ -448,84 +448,135 @@ class main_view implementation. me->a_ui5_client = i_ui5_client. - me->a_parser = z2ui5_cl_xml_view=>factory( ). - - data(shell) = me->a_parser->shell( appwidthlimited = me->a_ui5_client->_bind_edit( i_state->page-app_width_limited ) ). - - data(page) = shell->page( title = 'Native SQL Console'(001) ). - - data(header_content) = page->header_content( ). - - data(overflow_toolbar) = header_content->overflow_toolbar( ). - - overflow_toolbar->label( 'Fallback Limit'(002) - )->input( width = `15%` value = me->a_ui5_client->_bind_edit( i_state->sql_editor_pane-fallback_max_rows ) - )->button( text = 'Run'(003) press = me->a_ui5_client->_event( on_run=>event_name( ) ) type = `Emphasized` ##NO_TEXT - )->toolbar_spacer( - )->label( text = `Shell` ##NO_TEXT - )->switch( state = me->a_ui5_client->_bind_edit( i_state->page-app_width_limited ) - )->link( text = 'Project on GitHub'(004) target = '_blank' href = 'https://github.com/abap2UI5-addons/sql-console' ). - - data(flex_box) = page->flex_box( height = `100%` fitcontainer = abap_true rendertype = `Bare` ) ##NO_TEXT. - - data(responsive_splitter) = flex_box->responsive_splitter( defaultpane = `default` height = `100%` ) ##NO_TEXT. - - data(vertical_pane_container) = responsive_splitter->pane_container( orientation = `Vertical` ) ##NO_TEXT. - - data(horizontal_pane_container) = vertical_pane_container->pane_container( orientation = `Horizontal` ) ##NO_TEXT. + me->a_parser = z2ui5_cl_ui5_view_builder=>factory( + )->ele( n = `View` ns = `mvc` + )->a( n = `xmlns` v = `sap.m` + )->a( n = `xmlns:mvc` v = `sap.ui.core.mvc` + )->a( n = `xmlns:core` v = `sap.ui.core` + )->a( n = `xmlns:editor` v = `sap.ui.codeeditor` + )->a( n = `xmlns:layout` v = `sap.ui.layout` + )->a( n = `xmlns:table` v = `sap.ui.table` + )->a( n = `xmlns:z2ui5` v = `z2ui5.cc` + )->a( n = `displayBlock` v = `true` + )->a( n = `height` v = `100%` ). + + data(shell) = me->a_parser->ele( `Shell` + )->a( n = `appWidthLimited` v = me->a_ui5_client->_bind_edit( i_state->page-app_width_limited ) ). + + data(page) = shell->ele( `Page` + )->a( n = `title` v = 'Native SQL Console'(001) ). + + data(header_content) = page->ele( `headerContent` ). + + data(overflow_toolbar) = header_content->ele( `OverflowToolbar` ). + + overflow_toolbar->tag( `Label` + )->a( n = `text` v = 'Fallback Limit'(002) + )->tag( `Input` + )->a( n = `width` v = `15%` + )->a( n = `value` v = me->a_ui5_client->_bind_edit( i_state->sql_editor_pane-fallback_max_rows ) + )->tag( `Button` + )->a( n = `text` v = 'Run'(003) + )->a( n = `press` v = me->a_ui5_client->_event( on_run=>event_name( ) ) + )->a( n = `type` v = `Emphasized` ##NO_TEXT + )->tag( `ToolbarSpacer` + )->tag( `Label` + )->a( n = `text` v = `Shell` ##NO_TEXT + )->tag( `Switch` + )->a( n = `state` v = me->a_ui5_client->_bind_edit( i_state->page-app_width_limited ) + )->tag( `Link` + )->a( n = `text` v = 'Project on GitHub'(004) + )->a( n = `target` v = '_blank' + )->a( n = `href` v = 'https://github.com/abap2UI5-addons/sql-console' ). + + data(flex_box) = page->ele( `FlexBox` + )->a( n = `height` v = `100%` + )->a( n = `fitContainer` b = abap_true + )->a( n = `renderType` v = `Bare` ) ##NO_TEXT. + + data(responsive_splitter) = flex_box->ele( n = `ResponsiveSplitter` ns = `layout` + )->a( n = `defaultPane` v = `default` + )->a( n = `height` v = `100%` ) ##NO_TEXT. + + data(vertical_pane_container) = responsive_splitter->ele( n = `PaneContainer` ns = `layout` + )->a( n = `orientation` v = `Vertical` ) ##NO_TEXT. + + data(horizontal_pane_container) = vertical_pane_container->ele( n = `PaneContainer` ns = `layout` + )->a( n = `orientation` v = `Horizontal` ) ##NO_TEXT. "SQL Editor Pane - data(editor_split_pane) = horizontal_pane_container->split_pane( requiredparentwidth = `600` ). + data(editor_split_pane) = horizontal_pane_container->ele( n = `SplitPane` ns = `layout` + )->a( n = `requiredParentWidth` v = `600` ). - data(esp_layout_data) = editor_split_pane->layout_data( ns = `layout` ) ##NO_TEXT. + data(esp_layout_data) = editor_split_pane->ele( n = `layoutData` ns = `layout` ) ##NO_TEXT. - esp_layout_data->splitter_layout_data( size = me->a_ui5_client->_bind_edit( i_state->sql_editor_pane-layout_size ) ). + esp_layout_data->ele( n = `SplitterLayoutData` ns = `layout` + )->a( n = `size` v = me->a_ui5_client->_bind_edit( i_state->sql_editor_pane-layout_size ) ). - editor_split_pane->code_editor( type = `sql` value = me->a_ui5_client->_bind_edit( i_state->sql_editor_pane-statement ) ). + editor_split_pane->tag( n = `CodeEditor` ns = `editor` + )->a( n = `type` v = `sql` + )->a( n = `value` v = me->a_ui5_client->_bind_edit( i_state->sql_editor_pane-statement ) ). "History Pane - data(history_split_pane) = horizontal_pane_container->split_pane( requiredparentwidth = `400` ). + data(history_split_pane) = horizontal_pane_container->ele( n = `SplitPane` ns = `layout` + )->a( n = `requiredParentWidth` v = `400` ). - data(h_layout_data) = history_split_pane->layout_data( ns = `layout` ) ##NO_TEXT. + data(h_layout_data) = history_split_pane->ele( n = `layoutData` ns = `layout` ) ##NO_TEXT. - h_layout_data->splitter_layout_data( size = me->a_ui5_client->_bind_edit( i_state->history_pane-layout_size ) ). + h_layout_data->ele( n = `SplitterLayoutData` ns = `layout` + )->a( n = `size` v = me->a_ui5_client->_bind_edit( i_state->history_pane-layout_size ) ). - data(h_list) = history_split_pane->list( items = me->a_ui5_client->_bind_edit( i_state->history_pane-items ) - mode = `MultiSelect` - sticky = `ColumnHeaders,HeaderToolbar` ). + data(h_list) = history_split_pane->ele( `List` + )->a( n = `items` v = me->a_ui5_client->_bind_edit( i_state->history_pane-items ) + )->a( n = `mode` v = `MultiSelect` + )->a( n = `sticky` v = `ColumnHeaders,HeaderToolbar` ). - data(h_list_header_toolbar) = h_list->header_toolbar( ). + data(h_list_header_toolbar) = h_list->ele( `headerToolbar` ). - data(hlt_overflow_toolbar) = h_list_header_toolbar->overflow_toolbar( ). + data(hlt_overflow_toolbar) = h_list_header_toolbar->ele( `OverflowToolbar` ). - hlt_overflow_toolbar->title( 'Query History'(006) ). + hlt_overflow_toolbar->tag( `Title` + )->a( n = `text` v = 'Query History'(006) ). - hlt_overflow_toolbar->toolbar_spacer( ). + hlt_overflow_toolbar->tag( `ToolbarSpacer` ). - hlt_overflow_toolbar->button( press = me->a_ui5_client->_event( on_select_all_history_items=>event_name( ) ) icon = `sap-icon://multiselect-all` ) ##NO_TEXT. + hlt_overflow_toolbar->tag( `Button` + )->a( n = `press` v = me->a_ui5_client->_event( on_select_all_history_items=>event_name( ) ) + )->a( n = `icon` v = `sap-icon://multiselect-all` ) ##NO_TEXT. - hlt_overflow_toolbar->button( press = me->a_ui5_client->_event( on_deselect_all_history_items=>event_name( ) ) icon = `sap-icon://multiselect-none` ) ##NO_TEXT. + hlt_overflow_toolbar->tag( `Button` + )->a( n = `press` v = me->a_ui5_client->_event( on_deselect_all_history_items=>event_name( ) ) + )->a( n = `icon` v = `sap-icon://multiselect-none` ) ##NO_TEXT. - hlt_overflow_toolbar->button( text = 'Delete'(007) press = me->a_ui5_client->_event( on_delete_history_items=>event_name( ) ) icon = `sap-icon://delete` ) ##NO_TEXT. + hlt_overflow_toolbar->tag( `Button` + )->a( n = `text` v = 'Delete'(007) + )->a( n = `press` v = me->a_ui5_client->_event( on_delete_history_items=>event_name( ) ) + )->a( n = `icon` v = `sap-icon://delete` ) ##NO_TEXT. - h_list->standard_list_item( type = `Navigation` ##NO_TEXT - title = '{NATURAL_ID} - {CREATED_AT}' - description = '{SQL_STATEMENT}' - info = '{ROWS_NO}' - infostate = '{INFOSTATE}' - highlight = '{HIGHLIGHT}' - press = me->a_ui5_client->_event( val = on_load_history_item=>event_name( ) - t_arg = value #( ( `${ID}` ) ) ) - selected = `{SELECTED}` ). + h_list->tag( `StandardListItem` + )->a( n = `type` v = `Navigation` ##NO_TEXT + )->a( n = `title` v = '{NATURAL_ID} - {CREATED_AT}' + )->a( n = `description` v = '{SQL_STATEMENT}' + )->a( n = `info` v = '{ROWS_NO}' + )->a( n = `infoState` v = '{INFOSTATE}' + )->a( n = `highlight` v = '{HIGHLIGHT}' + )->a( n = `press` v = me->a_ui5_client->_event( val = on_load_history_item=>event_name( ) + t_arg = value #( ( `${ID}` ) ) ) + )->a( n = `selected` v = `{SELECTED}` ). "Results Pane - data(results_split_pane) = vertical_pane_container->split_pane( requiredparentwidth = `400` ). + data(results_split_pane) = vertical_pane_container->ele( n = `SplitPane` ns = `layout` + )->a( n = `requiredParentWidth` v = `400` ). - data(rsp_layout_data) = results_split_pane->layout_data( ns = `layout` ) ##NO_TEXT. + data(rsp_layout_data) = results_split_pane->ele( n = `layoutData` ns = `layout` ) ##NO_TEXT. - rsp_layout_data->splitter_layout_data( size = me->a_ui5_client->_bind_edit( i_state->results_pane-layout_size ) ). + rsp_layout_data->ele( n = `SplitterLayoutData` ns = `layout` + )->a( n = `size` v = me->a_ui5_client->_bind_edit( i_state->results_pane-layout_size ) ). - results_split_pane->vbox( id = `preview` fitcontainer = abap_true direction = `Row` ) ##NO_TEXT. + results_split_pane->ele( `VBox` + )->a( n = `id` v = `preview` + )->a( n = `fitContainer` b = abap_true + )->a( n = `direction` v = `Row` ) ##NO_TEXT. new data_result_view( i_state = i_state i_ui5_client = i_ui5_client )->set_for_display( ). @@ -548,7 +599,17 @@ class data_result_view implementation. me->a_ui5_client = i_ui5_client. - me->a_parser = z2ui5_cl_xml_view=>factory( ). + me->a_parser = z2ui5_cl_ui5_view_builder=>factory( + )->ele( n = `View` ns = `mvc` + )->a( n = `xmlns` v = `sap.m` + )->a( n = `xmlns:mvc` v = `sap.ui.core.mvc` + )->a( n = `xmlns:core` v = `sap.ui.core` + )->a( n = `xmlns:editor` v = `sap.ui.codeeditor` + )->a( n = `xmlns:layout` v = `sap.ui.layout` + )->a( n = `xmlns:table` v = `sap.ui.table` + )->a( n = `xmlns:z2ui5` v = `z2ui5.cc` + )->a( n = `displayBlock` v = `true` + )->a( n = `height` v = `100%` ). data(app) = cast zcl_2ui5_native_sql_console( i_ui5_client->get_app( i_ui5_client->get( )-s_draft-id ) ). app->mr_output_data = i_state->results_pane-output_data. @@ -558,60 +619,71 @@ class data_result_view implementation. data(fields) = z2ui5_cl_util=>rtti_get_t_attri_by_any( ). - data(table) = me->a_parser->ui_table( id = `previewTab` - rows = me->a_ui5_client->_bind( ) - editable = abap_false - alternaterowcolors = abap_true - showcolumnvisibilitymenu = abap_true - enablegrouping = abap_true - enableselectall = abap_true - enablecellfilter = abap_true - selectionbehavior = `RowOnly` - visiblerowcountmode = `Auto` - selectionmode = `MultiToggle` ) ##NO_TEXT. - - data(table_extension) = table->ui_extension( ). - - data(te_overflow_toolbar) = table_extension->overflow_toolbar( width = `100%` ). - - te_overflow_toolbar->title( me->a_ui5_client->_bind( i_state->results_pane-title ) ). - - te_overflow_toolbar->toolbar_spacer( ). - - te_overflow_toolbar->input( width = `50%` - value = me->a_ui5_client->_bind_edit( i_state->results_pane-wide_filter_string ) - description = 'Filter any column on enter'(008) - submit = me->a_ui5_client->_event( on_wide_filtering=>event_name( ) ) ). - - te_overflow_toolbar->toolbar_spacer( ). - - te_overflow_toolbar->_z2ui5( )->spreadsheet_export( tableid = `previewTab` - icon = 'sap-icon://excel-attachment' - type = `Emphasized` ##NO_TEXT - columnconfig = me->a_ui5_client->_bind( val = i_state->results_pane-column_config - custom_filter = new z2ui5_cl_cc_spreadsheet( ) + data(table) = me->a_parser->ele( n = `Table` ns = `table` + )->a( n = `id` v = `previewTab` + )->a( n = `rows` v = me->a_ui5_client->_bind( ) + )->a( n = `editable` b = abap_false + )->a( n = `alternateRowColors` b = abap_true + )->a( n = `showColumnVisibilityMenu` b = abap_true + )->a( n = `enableGrouping` b = abap_true + )->a( n = `enableSelectAll` b = abap_true + )->a( n = `enableCellFilter` b = abap_true + )->a( n = `selectionBehavior` v = `RowOnly` + )->a( n = `visibleRowCountMode` v = `Auto` + )->a( n = `selectionMode` v = `MultiToggle` ) ##NO_TEXT. + + data(table_extension) = table->ele( n = `extension` ns = `table` ). + + data(te_overflow_toolbar) = table_extension->ele( `OverflowToolbar` + )->a( n = `width` v = `100%` ). + + te_overflow_toolbar->tag( `Title` + )->a( n = `text` v = me->a_ui5_client->_bind( i_state->results_pane-title ) ). + + te_overflow_toolbar->tag( `ToolbarSpacer` ). + + te_overflow_toolbar->tag( `Input` + )->a( n = `width` v = `50%` + )->a( n = `value` v = me->a_ui5_client->_bind_edit( i_state->results_pane-wide_filter_string ) + )->a( n = `description` v = 'Filter any column on enter'(008) + )->a( n = `submit` v = me->a_ui5_client->_event( on_wide_filtering=>event_name( ) ) ). + + te_overflow_toolbar->tag( `ToolbarSpacer` ). + + te_overflow_toolbar->tag( n = `ExportSpreadsheet` ns = `z2ui5` + )->a( n = `tableId` v = `previewTab` + )->a( n = `icon` v = 'sap-icon://excel-attachment' + )->a( n = `type` v = `Emphasized` ##NO_TEXT + )->a( n = `columnconfig` v = me->a_ui5_client->_bind( val = i_state->results_pane-column_config + custom_filter = new z2ui5_cl_cci_json_filter( ) custom_mapper = z2ui5_cl_ajson_mapping=>create_lower_case( ) ) ). - data(columns) = table->ui_columns( ). + data(columns) = table->ele( n = `columns` ns = `table` ). loop at fields reference into data(field). - data(column) = columns->ui_column( width = `auto` ##NO_TEXT - sortproperty = field->*-name - filterproperty = field->*-name - autoresizable = abap_true ). - - column->text( text = field->*-name - emptyindicatormode = abap_true - renderwhitespace = abap_true - wrapping = abap_false )->ui_template( )->label( text = `{` && field->*-name && `}` - wrapping = abap_false ). + data(column) = columns->ele( n = `Column` ns = `table` + )->a( n = `width` v = `auto` ##NO_TEXT + )->a( n = `sortProperty` v = field->*-name + )->a( n = `filterProperty` v = field->*-name + )->a( n = `autoResizable` b = abap_true ). + + column->tag( `Text` + )->a( n = `text` v = field->*-name + )->a( n = `emptyIndicatorMode` b = abap_true + )->a( n = `renderWhitespace` b = abap_true + )->a( n = `wrapping` b = abap_false + )->ele( n = `template` ns = `table` + )->tag( `Label` + )->a( n = `text` v = `{` && field->*-name && `}` + )->a( n = `wrapping` b = abap_false ). endloop. else. - me->a_parser->text( 'Data preview...'(009) ). + me->a_parser->tag( `Text` + )->a( n = `text` v = 'Data preview...'(009) ). endif.