From d7cf69d83118b405ed71e7dde218e08868e49754 Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Wed, 20 Dec 2023 11:58:53 -0500 Subject: [PATCH 01/32] Set `isReadOnly` conditions --- web/app/components/project/index.hbs | 25 +++++---- web/app/components/project/index.ts | 8 +++ .../authenticated/projects/project-test.ts | 53 ++++++++++++++++++- 3 files changed, 73 insertions(+), 13 deletions(-) diff --git a/web/app/components/project/index.hbs b/web/app/components/project/index.hbs index 4f55ec248..d57e1f56c 100644 --- a/web/app/components/project/index.hbs +++ b/web/app/components/project/index.hbs @@ -78,20 +78,23 @@ @tag="h1" @placeholder="Add project title" @onSave={{this.saveTitle}} + @isReadOnly={{not this.projectIsActive}} @isRequired={{true}} /> - - {{! Description }} -
- -
+ {{#if (or this.projectIsActive this.description)}} + {{! Description }} +
+ +
+ {{/if}} {{#if this.jiraIsEnabled}} diff --git a/web/app/components/project/index.ts b/web/app/components/project/index.ts index ac6e3a470..4c6539332 100644 --- a/web/app/components/project/index.ts +++ b/web/app/components/project/index.ts @@ -94,6 +94,14 @@ export default class ProjectIndexComponent extends Component Date: Wed, 20 Dec 2023 12:19:17 -0500 Subject: [PATCH 02/32] Add isReadOnly conditions to project resources --- web/app/components/project/index.hbs | 2 ++ web/app/components/project/resource.hbs | 4 +++- web/app/components/project/resource.ts | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/web/app/components/project/index.hbs b/web/app/components/project/index.hbs index 4f55ec248..73a26c6c9 100644 --- a/web/app/components/project/index.hbs +++ b/web/app/components/project/index.hbs @@ -149,6 +149,7 @@ {{#each this.hermesDocuments as |document|}}
  • + {{#unless @isReadOnly}} + + {{/unless}} diff --git a/web/app/components/project/resource.ts b/web/app/components/project/resource.ts index 1f8fbdf48..82cc7e1c7 100644 --- a/web/app/components/project/resource.ts +++ b/web/app/components/project/resource.ts @@ -5,6 +5,7 @@ interface ProjectResourceComponentSignature { Element: HTMLDivElement; Args: { overflowMenuItems: Record; + isReadOnly?: boolean; }; Blocks: { default: []; From 3d0839929403d101b474a2fefa5d9f6ad4da5365 Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Wed, 20 Dec 2023 12:25:59 -0500 Subject: [PATCH 03/32] Add tests --- .../authenticated/projects/project-test.ts | 51 ++++++++++++++++--- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/web/tests/acceptance/authenticated/projects/project-test.ts b/web/tests/acceptance/authenticated/projects/project-test.ts index dd87f2606..e179c437c 100644 --- a/web/tests/acceptance/authenticated/projects/project-test.ts +++ b/web/tests/acceptance/authenticated/projects/project-test.ts @@ -1,14 +1,7 @@ import { MirageTestContext, setupMirage } from "ember-cli-mirage/test-support"; import { authenticateSession } from "ember-simple-auth/test-support"; import { module, test } from "qunit"; -import { - click, - fillIn, - rerender, - settled, - visit, - waitFor, -} from "@ember/test-helpers"; +import { click, fillIn, visit, waitFor } from "@ember/test-helpers"; import { getPageTitle } from "ember-page-title/test-support"; import { setupApplicationTest } from "ember-qunit"; import { ProjectStatus } from "hermes/types/project-status"; @@ -406,6 +399,25 @@ module("Acceptance | authenticated/projects/project", function (hooks) { assert.equal(projectDocuments.length, 0); }); + test("documents can only be removed if the project is active", async function (this: AuthenticatedProjectsProjectRouteTestContext, assert) { + await visit("/projects/1"); + + assert.dom(DOCUMENT_LIST_ITEM).exists(); + assert.dom(OVERFLOW_MENU_BUTTON).exists(); + + await click(STATUS_TOGGLE); + await click(COMPLETED_STATUS_ACTION); + + assert.dom(DOCUMENT_LIST_ITEM).exists(); + assert.dom(OVERFLOW_MENU_BUTTON).doesNotExist(); + + await click(STATUS_TOGGLE); + await click(ARCHIVED_STATUS_ACTION); + + assert.dom(DOCUMENT_LIST_ITEM).exists(); + assert.dom(OVERFLOW_MENU_BUTTON).doesNotExist(); + }); + test("you can add external links to a project", async function (this: AuthenticatedProjectsProjectRouteTestContext, assert) { const project = this.server.schema.projects.first(); @@ -519,6 +531,29 @@ module("Acceptance | authenticated/projects/project", function (hooks) { assert.equal(projectLinks.length, 0); }); + test("external links can only be edited if the project is active", async function (this: AuthenticatedProjectsProjectRouteTestContext, assert) { + this.server.schema.projects.first().update({ + externalLinks: [this.server.create("related-external-link").attrs], + }); + + await visit("/projects/1"); + + assert.dom(EXTERNAL_LINK).exists(); + assert.dom(OVERFLOW_MENU_BUTTON).exists(); + + await click(STATUS_TOGGLE); + await click(COMPLETED_STATUS_ACTION); + + assert.dom(EXTERNAL_LINK).exists(); + assert.dom(OVERFLOW_MENU_BUTTON).doesNotExist(); + + await click(STATUS_TOGGLE); + await click(ARCHIVED_STATUS_ACTION); + + assert.dom(EXTERNAL_LINK).exists(); + assert.dom(OVERFLOW_MENU_BUTTON).doesNotExist(); + }); + test("you can't save an empty project title", async function (this: AuthenticatedProjectsProjectRouteTestContext, assert) { await visit("/projects/1"); From 6c431d8e43ce00e4dee466d93cbbb383b74cbf76 Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Wed, 20 Dec 2023 12:44:55 -0500 Subject: [PATCH 04/32] Conditionally disable plus button --- web/app/components/project/index.hbs | 19 +++++++++---------- .../authenticated/projects/project-test.ts | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/web/app/components/project/index.hbs b/web/app/components/project/index.hbs index ad5ea675b..2dbc6689f 100644 --- a/web/app/components/project/index.hbs +++ b/web/app/components/project/index.hbs @@ -119,16 +119,15 @@ @addResource={{this.addResource}} > <:header as |rr|> -
    -
    - - - -
    +
    + + +
    diff --git a/web/tests/acceptance/authenticated/projects/project-test.ts b/web/tests/acceptance/authenticated/projects/project-test.ts index e179c437c..57de47b63 100644 --- a/web/tests/acceptance/authenticated/projects/project-test.ts +++ b/web/tests/acceptance/authenticated/projects/project-test.ts @@ -554,6 +554,22 @@ module("Acceptance | authenticated/projects/project", function (hooks) { assert.dom(OVERFLOW_MENU_BUTTON).doesNotExist(); }); + test('the "add resource" button is disabled when the project is inactive', async function (this: AuthenticatedProjectsProjectRouteTestContext, assert) { + await visit("/projects/1"); + + assert.dom(ADD_RESOURCE_BUTTON).isNotDisabled(); + + await click(STATUS_TOGGLE); + await click(COMPLETED_STATUS_ACTION); + + assert.dom(ADD_RESOURCE_BUTTON).isDisabled(); + + await click(STATUS_TOGGLE); + await click(ACTIVE_STATUS_ACTION); + + assert.dom(ADD_RESOURCE_BUTTON).isNotDisabled(); + }); + test("you can't save an empty project title", async function (this: AuthenticatedProjectsProjectRouteTestContext, assert) { await visit("/projects/1"); From 43d1a31bd787e203b4a3d906a44ea40bd30df830 Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Thu, 21 Dec 2023 09:17:53 -0500 Subject: [PATCH 05/32] Update project-test.ts --- web/tests/acceptance/authenticated/projects/project-test.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/web/tests/acceptance/authenticated/projects/project-test.ts b/web/tests/acceptance/authenticated/projects/project-test.ts index de8480526..be5748a9b 100644 --- a/web/tests/acceptance/authenticated/projects/project-test.ts +++ b/web/tests/acceptance/authenticated/projects/project-test.ts @@ -582,6 +582,11 @@ module("Acceptance | authenticated/projects/project", function (hooks) { assert.dom(ADD_RESOURCE_BUTTON).isDisabled(); + await click(STATUS_TOGGLE); + await click(ARCHIVED_STATUS_ACTION); + + assert.dom(ADD_RESOURCE_BUTTON).isDisabled(); + await click(STATUS_TOGGLE); await click(ACTIVE_STATUS_ACTION); From 9293f04c008cbe851a7523aa814e56ffbc9523bb Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Thu, 21 Dec 2023 15:21:45 -0500 Subject: [PATCH 06/32] Add read-only styles to Jira --- web/app/components/overflow-menu.hbs | 4 +- web/app/components/overflow-menu.ts | 3 + web/app/components/project/index.hbs | 1 + web/app/components/project/jira-widget.hbs | 403 +++++++++--------- web/app/components/project/jira-widget.ts | 1 + web/app/components/project/resource.hbs | 5 +- web/app/styles/components/jira-widget.scss | 5 + .../authenticated/projects/project-test.ts | 25 +- .../components/project/jira-widget-test.ts | 26 +- .../related-resources/overflow-menu-test.ts | 25 ++ 10 files changed, 270 insertions(+), 228 deletions(-) diff --git a/web/app/components/overflow-menu.hbs b/web/app/components/overflow-menu.hbs index 1e7003679..30dea88c7 100644 --- a/web/app/components/overflow-menu.hbs +++ b/web/app/components/overflow-menu.hbs @@ -2,14 +2,14 @@ @items={{@items}} @renderOut={{true}} @placement="bottom-end" - @offset={{hash mainAxis=-3 crossAxis=-3}} + @offset={{or @offset (hash mainAxis=-3 crossAxis=-3)}} data-test-overflow-menu ...attributes > <:anchor as |dd|>
    ; + offset?: OffsetOptions; + isShown?: boolean; }; } diff --git a/web/app/components/project/index.hbs b/web/app/components/project/index.hbs index 65ec77dc8..376d2adbc 100644 --- a/web/app/components/project/index.hbs +++ b/web/app/components/project/index.hbs @@ -107,6 +107,7 @@ @onIssueRemove={{this.removeJiraIssue}} @onIssueSelect={{this.addJiraIssue}} @isLoading={{this.loadJiraIssue.isRunning}} + @isReadOnly={{not this.projectIsActive}} />
    {{/if}} diff --git a/web/app/components/project/jira-widget.hbs b/web/app/components/project/jira-widget.hbs index 1342e767a..91b077ed9 100644 --- a/web/app/components/project/jira-widget.hbs +++ b/web/app/components/project/jira-widget.hbs @@ -1,232 +1,213 @@ -
    +{{#unless (and (not this.issue) (not @isLoading) @isReadOnly)}}
    +
    - {{#unless (or (not this.issue) @isNewProjectForm)}} -
    -
    - -
    + {{#unless (or (not this.issue) @isNewProjectForm)}}
    -
    - {{/unless}} - - {{#if this.issue}} - - - {{! Content }} -
    - {{this.issueType}} -
    - - {{this.issue.key}} - - - {{this.issue.summary}} - + data-test-jira-icon + class="relative z-10 mr-3 flex h-6 items-center" + > +
    +
    +
    + {{/unless}} - {{#if (or this.issuePriority this.issueAssignee this.issueStatus)}} -
    - {{#if this.issuePriority}} - {{this.issuePriority}} - {{/if}} - {{#if this.issueAssignee}} - - {{/if}} - {{#if this.issueStatus}} -
    + + {{! Content }} +
    + {{this.issueType}} +
    + - {{this.issueStatus}} -
    - {{/if}} + {{this.issue.key}} + + + {{this.issue.summary}} + +
    - {{/if}} - - - <:anchor as |dd|> - - - - - <:item as |dd|> - - - {{dd.attrs.label}} - - - - {{else if @isLoading}} - - {{else}} - {{! + Add Jira issue }} - - <:anchor as |dd|> -
    - {{#if this.inputIsShown}} - - {{! search icon }} -
    - + {{#if this.issuePriority}} + {{this.issuePriority}} -
    - {{else}} - - + {{/if}} + {{#if this.issueStatus}} +
    + {{this.issueStatus}} +
    + {{/if}} +
    + {{/if}} + + {{#unless @isReadOnly}} + + {{/unless}} + {{else if @isLoading}} + + {{else}} + {{! + Add Jira issue }} + + <:anchor as |dd|> +
    + {{#if this.inputIsShown}} + - Add Jira issue... - - {{/if}} + {{! search icon }} +
    + +
    + {{else}} + + + Add Jira issue... + + {{/if}} - {{#if this.searchJiraIssues.isRunning}} + {{#if this.searchJiraIssues.isRunning}} +
    + +
    + {{/if}} +
    + + <:no-matches> + {{#unless (lt this.query.length 1)}}
    - + {{#unless this.searchJiraIssues.isRunning}} + No matches + {{/unless}}
    - {{/if}} -
    - - <:no-matches> - {{#unless (lt this.query.length 1)}} -
    - {{#unless this.searchJiraIssues.isRunning}} - No matches - {{/unless}} -
    - {{/unless}} - - <:item as |dd|> - -
    - -
    - {{dd.attrs.key}} - {{dd.attrs.summary}} + {{/unless}} + + <:item as |dd|> + +
    + +
    + {{dd.attrs.key}} + {{dd.attrs.summary}} +
    -
    - - - - {{/if}} + + + + {{/if}} +
    -
    +{{/unless}} diff --git a/web/app/components/project/jira-widget.ts b/web/app/components/project/jira-widget.ts index 818feda2d..62b9e8284 100644 --- a/web/app/components/project/jira-widget.ts +++ b/web/app/components/project/jira-widget.ts @@ -18,6 +18,7 @@ interface ProjectJiraWidgetComponentSignature { onIssueRemove?: () => void; isDisabled?: boolean; isLoading?: boolean; + isReadOnly?: boolean; /** * Whether the component is being used in a form context. * If true, removes the leading Jira icon and shows the input, autofocused, diff --git a/web/app/components/project/resource.hbs b/web/app/components/project/resource.hbs index 59e5447a4..ac6228070 100644 --- a/web/app/components/project/resource.hbs +++ b/web/app/components/project/resource.hbs @@ -3,6 +3,9 @@ {{yield}} {{#unless @isReadOnly}} - + {{/unless}}
    diff --git a/web/app/styles/components/jira-widget.scss b/web/app/styles/components/jira-widget.scss index 9fcc4b128..b0f088f6b 100644 --- a/web/app/styles/components/jira-widget.scss +++ b/web/app/styles/components/jira-widget.scss @@ -17,10 +17,15 @@ } } } + + .overflow-button-container { + @apply relative top-auto right-auto mr-1 ml-2 w-auto bg-none; + } } .add-jira-issue-button { @apply py-0 pl-[8px] pr-[14px]; + &:hover, &:focus { @apply rounded-button-md shadow-surface-low; diff --git a/web/tests/acceptance/authenticated/projects/project-test.ts b/web/tests/acceptance/authenticated/projects/project-test.ts index 08c54c89f..f8718f2b6 100644 --- a/web/tests/acceptance/authenticated/projects/project-test.ts +++ b/web/tests/acceptance/authenticated/projects/project-test.ts @@ -67,7 +67,7 @@ const EXTERNAL_LINK_COUNT = "[data-test-external-link-count]"; const EXTERNAL_LINK_LIST = "[data-test-external-link-list]"; const DOCUMENT_LIST_ITEM = "[data-test-document-list-item]"; -const OVERFLOW_MENU_BUTTON = "[data-test-overflow-menu-button]"; +const DOCUMENT_OVERFLOW_MENU_BUTTON = `${DOCUMENT_LIST_ITEM} [data-test-overflow-menu-button]`; const OVERFLOW_MENU_EDIT = "[data-test-overflow-menu-action='edit']"; const OVERFLOW_MENU_REMOVE = "[data-test-overflow-menu-action='remove']"; @@ -89,12 +89,13 @@ const EXTERNAL_LINK = "[data-test-related-link]"; const STATUS_TOGGLE = "[data-test-project-status-toggle]"; const COPY_URL_BUTTON = "[data-test-copy-url-button]"; +const JIRA_WIDGET = "[data-test-jira-widget]"; const ADD_JIRA_BUTTON = "[data-test-add-jira-button]"; const ADD_JIRA_INPUT = "[data-test-add-jira-input]"; const JIRA_PICKER_RESULT = "[data-test-jira-picker-result]"; const JIRA_ISSUE_TYPE_ICON = "[data-test-jira-issue-type-icon]"; -const JIRA_OVERFLOW_BUTTON = "[data-test-jira-overflow-button]"; +const JIRA_OVERFLOW_BUTTON = `${JIRA_WIDGET} [data-test-overflow-menu-button]`; const JIRA_LINK = "[data-test-jira-link]"; const JIRA_PRIORITY_ICON = "[data-test-jira-priority-icon]"; const JIRA_ASSIGNEE_AVATAR = "[data-test-jira-assignee-avatar-wrapper] img"; @@ -102,7 +103,7 @@ const JIRA_STATUS = "[data-test-jira-status]"; const JIRA_TYPE_ICON = "[data-test-jira-issue-type-icon]"; const JIRA_KEY = "[data-test-jira-key]"; const JIRA_SUMMARY = "[data-test-jira-summary]"; -const JIRA_REMOVE_BUTTON = "[data-test-remove-button]"; +const JIRA_REMOVE_BUTTON = "[data-test-overflow-menu-action='remove']"; const ACTIVE_STATUS_ACTION = "[data-test-status-action='active']"; const COMPLETED_STATUS_ACTION = "[data-test-status-action='completed']"; @@ -413,7 +414,7 @@ module("Acceptance | authenticated/projects/project", function (hooks) { assert.dom(DOCUMENT_LIST_ITEM).exists({ count: 1 }); - await click(OVERFLOW_MENU_BUTTON); + await click(DOCUMENT_OVERFLOW_MENU_BUTTON); await click(OVERFLOW_MENU_REMOVE); assert.dom(DOCUMENT_LIST_ITEM).doesNotExist(); @@ -428,19 +429,19 @@ module("Acceptance | authenticated/projects/project", function (hooks) { await visit("/projects/1"); assert.dom(DOCUMENT_LIST_ITEM).exists(); - assert.dom(OVERFLOW_MENU_BUTTON).exists(); + assert.dom(DOCUMENT_OVERFLOW_MENU_BUTTON).exists(); await click(STATUS_TOGGLE); await click(COMPLETED_STATUS_ACTION); assert.dom(DOCUMENT_LIST_ITEM).exists(); - assert.dom(OVERFLOW_MENU_BUTTON).doesNotExist(); + assert.dom(DOCUMENT_OVERFLOW_MENU_BUTTON).doesNotExist(); await click(STATUS_TOGGLE); await click(ARCHIVED_STATUS_ACTION); assert.dom(DOCUMENT_LIST_ITEM).exists(); - assert.dom(OVERFLOW_MENU_BUTTON).doesNotExist(); + assert.dom(DOCUMENT_OVERFLOW_MENU_BUTTON).doesNotExist(); }); test("you can add external links to a project", async function (this: AuthenticatedProjectsProjectRouteTestContext, assert) { @@ -500,7 +501,7 @@ module("Acceptance | authenticated/projects/project", function (hooks) { assert.dom(EXTERNAL_LINK).exists({ count: 1 }); - await click(OVERFLOW_MENU_BUTTON); + await click(DOCUMENT_OVERFLOW_MENU_BUTTON); await click(OVERFLOW_MENU_EDIT); const linkTitle = "Bar"; @@ -545,7 +546,7 @@ module("Acceptance | authenticated/projects/project", function (hooks) { assert.dom(EXTERNAL_LINK).exists({ count: 1 }); - await click(OVERFLOW_MENU_BUTTON); + await click(DOCUMENT_OVERFLOW_MENU_BUTTON); await click(OVERFLOW_MENU_REMOVE); assert.dom(EXTERNAL_LINK).doesNotExist(); @@ -564,19 +565,19 @@ module("Acceptance | authenticated/projects/project", function (hooks) { await visit("/projects/1"); assert.dom(EXTERNAL_LINK).exists(); - assert.dom(OVERFLOW_MENU_BUTTON).exists(); + assert.dom(DOCUMENT_OVERFLOW_MENU_BUTTON).exists(); await click(STATUS_TOGGLE); await click(COMPLETED_STATUS_ACTION); assert.dom(EXTERNAL_LINK).exists(); - assert.dom(OVERFLOW_MENU_BUTTON).doesNotExist(); + assert.dom(DOCUMENT_OVERFLOW_MENU_BUTTON).doesNotExist(); await click(STATUS_TOGGLE); await click(ARCHIVED_STATUS_ACTION); assert.dom(EXTERNAL_LINK).exists(); - assert.dom(OVERFLOW_MENU_BUTTON).doesNotExist(); + assert.dom(DOCUMENT_OVERFLOW_MENU_BUTTON).doesNotExist(); }); test("you can't save an empty project title", async function (this: AuthenticatedProjectsProjectRouteTestContext, assert) { diff --git a/web/tests/integration/components/project/jira-widget-test.ts b/web/tests/integration/components/project/jira-widget-test.ts index d30206817..0a170467f 100644 --- a/web/tests/integration/components/project/jira-widget-test.ts +++ b/web/tests/integration/components/project/jira-widget-test.ts @@ -17,8 +17,9 @@ const KEY = "[data-test-jira-key]"; const SUMMARY = "[data-test-jira-summary]"; const LINK = "[data-test-jira-link]"; const ISSUE_TYPE_ICON = "[data-test-jira-issue-type-icon]"; -const OVERFLOW_BUTTON = "[data-test-jira-overflow-button]"; -const REMOVE_JIRA_BUTTON = "[data-test-remove-button]"; +const JIRA_WIDGET = "[data-test-jira-widget]"; +const OVERFLOW_BUTTON = `${JIRA_WIDGET} [data-test-overflow-menu-button]`; +const REMOVE_JIRA_BUTTON = "[data-test-overflow-menu-action='remove']"; const PRIORITY_ICON = "[data-test-jira-priority-icon]"; const ASSIGNEE_AVATAR = "[data-test-jira-assignee-avatar-wrapper] img"; const STATUS = "[data-test-jira-status]"; @@ -487,4 +488,25 @@ module("Integration | Component | project/jira-widget", function (hooks) { "the search icon does not animate in the form context", ); }); + + test("it can be rendered read-only", async function (this: Context, assert) { + this.set("issue", this.server.create("jira-picker-result")); + + await render(hbs` + + `); + assert.dom(LINK).exists(); + assert.dom(OVERFLOW_BUTTON).doesNotExist(); + + this.set("issue", undefined); + + assert + .dom(LINK) + .doesNotExist( + "the link is not rendered if read-only with an undefined issue", + ); + }); }); diff --git a/web/tests/integration/components/related-resources/overflow-menu-test.ts b/web/tests/integration/components/related-resources/overflow-menu-test.ts index f623bc89e..d5b1cabd6 100644 --- a/web/tests/integration/components/related-resources/overflow-menu-test.ts +++ b/web/tests/integration/components/related-resources/overflow-menu-test.ts @@ -2,6 +2,7 @@ import { TestContext, click, findAll, render } from "@ember/test-helpers"; import { hbs } from "ember-cli-htmlbars"; import { setupRenderingTest } from "ember-qunit"; import { OverflowItem } from "hermes/components/overflow-menu"; +import htmlElement from "hermes/utils/html-element"; import { module, test } from "qunit"; const POPOVER = "[data-test-overflow-menu]"; @@ -12,6 +13,7 @@ const LABEL = `${POPOVER} [data-test-label]`; interface OverflowMenuTestContext extends TestContext { items: Record; + isShown?: boolean; } module("Integration | Component | related-resources/add", function (hooks) { @@ -71,4 +73,27 @@ module("Integration | Component | related-resources/add", function (hooks) { assert.equal(actionTwoCount, 1); }); + + test("it can be force-shown", async function (this: OverflowMenuTestContext, assert) { + this.set("isShown", false); + this.set("items", { + item1: { + label: "Item 1", + icon: "square", + action: () => {}, + }, + }); + await render(hbs` + + `); + + assert.dom(TOGGLE).hasStyle({ visibility: "hidden" }); + + this.set("isShown", true); + + assert.dom(TOGGLE).hasStyle({ visibility: "visible" }); + }); }); From a02e0a0bb99e5fd4ddf354b06ce14d9d27bee3e8 Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Thu, 21 Dec 2023 15:26:02 -0500 Subject: [PATCH 07/32] Start of animations --- web/app/components/project/index.hbs | 110 +++++++++++++++------------ web/app/components/project/index.ts | 44 +++++++++++ 2 files changed, 104 insertions(+), 50 deletions(-) diff --git a/web/app/components/project/index.hbs b/web/app/components/project/index.hbs index d8d549edd..656acad96 100644 --- a/web/app/components/project/index.hbs +++ b/web/app/components/project/index.hbs @@ -64,52 +64,55 @@
    -
    - {{! Title }} -
    - -
    - {{#if (or this.projectIsActive this.description)}} - {{! Description }} -
    + +
    + + {{! Title }} +
    - {{/if}} -
    - -{{#if this.jiraIsEnabled}} - {{! Jira }} -
    - + {{#animated-if (or this.projectIsActive this.description)}} + {{! Description }} +
    + +
    + {{/animated-if}}
    -{{/if}} + + {{#if this.jiraIsEnabled}} + {{! Jira }} +
    + +
    + {{/if}} +
    @@ -120,16 +123,23 @@ @addResource={{this.addResource}} > <:header as |rr|> -
    - - - -
    + {{#animated-if + this.projectIsActive + rules=this.plusButtonTransitionRules + initialInsertion=true + finalRemoval=true + }} +
    + + + +
    + {{/animated-if}} diff --git a/web/app/components/project/index.ts b/web/app/components/project/index.ts index 8388014c8..923a22731 100644 --- a/web/app/components/project/index.ts +++ b/web/app/components/project/index.ts @@ -21,6 +21,21 @@ import HermesFlashMessagesService from "hermes/services/flash-messages"; import { FLASH_MESSAGES_LONG_TIMEOUT } from "hermes/utils/ember-cli-flash/timeouts"; import updateRelatedResourcesSortOrder from "hermes/utils/update-related-resources-sort-order"; import Ember from "ember"; +import { TransitionContext, wait } from "ember-animated/."; +import { fadeIn, fadeOut } from "ember-animated/motions/opacity"; +import { emptyTransition } from "hermes/utils/ember-animated/empty-transition"; +import move from "ember-animated/motions/move"; +import { Resize } from "ember-animated/motions/resize"; +import { easeOutExpo } from "hermes/utils/ember-animated/easings"; + +class ResizeProject extends Resize { + *animate() { + this.opts.duration = Ember.testing ? 0 : 500; + this.opts.easing = easeOutExpo; + yield wait(100); + yield* super.animate(); + } +} interface ProjectIndexComponentSignature { Args: { @@ -39,6 +54,8 @@ export default class ProjectIndexComponent extends Component Date: Thu, 21 Dec 2023 17:22:51 -0500 Subject: [PATCH 08/32] Animation styles --- web/app/components/project/index.hbs | 56 +-- web/app/components/project/index.ts | 79 +++- web/app/components/project/jira-widget.hbs | 384 +++++++++--------- .../utils/ember-animated/animate-rotation.ts | 55 +++ 4 files changed, 351 insertions(+), 223 deletions(-) create mode 100644 web/app/utils/ember-animated/animate-rotation.ts diff --git a/web/app/components/project/index.hbs b/web/app/components/project/index.hbs index 8d437a58f..f1c60abbd 100644 --- a/web/app/components/project/index.hbs +++ b/web/app/components/project/index.hbs @@ -83,34 +83,43 @@ @isSaving={{this.titleIsSaving}} />
    - {{#animated-if (or this.projectIsActive this.description)}} - {{! Description }} -
    - -
    - {{/animated-if}} + + + {{#animated-if + (or this.projectIsActive this.description) + rules=this.descriptionTransitionRules + }} + {{! Description }} +
    + +
    + {{/animated-if}} +
    +
    {{#if this.jiraIsEnabled}} {{! Jira }}
    - + {{#animated-if this.jiraWidgetIsShown rules=this.jiraTransitionRules}} + + {{/animated-if}}
    {{/if}} @@ -131,6 +140,7 @@ finalRemoval=true }}
    +
    -
    - {{#unless (or (not this.issue) @isNewProjectForm)}} -
    -
    - -
    -
    + {{#unless (or (not this.issue) @isNewProjectForm)}} +
    +
    +
    - {{/unless}} +
    +
    + {{/unless}} - {{#if this.issue}} - + {{#if this.issue}} + - {{! Content }} -
    - {{this.issueType}} -
    + {{this.issueType}} +
    + - - {{this.issue.key}} - - - {{this.issue.summary}} - -
    + {{this.issue.key}} + + + {{this.issue.summary}} +
    +
    - {{#if (or this.issuePriority this.issueAssignee this.issueStatus)}} -
    - {{#if this.issuePriority}} - {{this.issuePriority}} - {{/if}} - {{#if this.issueAssignee}} - + {{#if this.issuePriority}} + {{this.issuePriority}} + {{/if}} + {{#if this.issueAssignee}} + + {{/if}} + {{#if this.issueStatus}} +
    + {{this.issueStatus}} +
    + {{/if}} +
    + {{/if}} +
    + {{#unless @isReadOnly}} + + {{/unless}} + {{else if @isLoading}} + + {{else}} + {{! + Add Jira issue }} + + <:anchor as |dd|> +
    + {{#if this.inputIsShown}} + + {{! search icon }} +
    + - {{/if}} - {{#if this.issueStatus}} -
    - {{this.issueStatus}} -
    - {{/if}} -
    - {{/if}} - - {{#unless @isReadOnly}} - - {{/unless}} - {{else if @isLoading}} - - {{else}} - {{! + Add Jira issue }} - - <:anchor as |dd|> -
    - {{#if this.inputIsShown}} - + {{else}} + + - {{! search icon }} -
    - -
    - {{else}} - - - Add Jira issue... - - {{/if}} + Add Jira issue... +
    + {{/if}} - {{#if this.searchJiraIssues.isRunning}} -
    - -
    - {{/if}} -
    - - <:no-matches> - {{#unless (lt this.query.length 1)}} + {{#if this.searchJiraIssues.isRunning}}
    - {{#unless this.searchJiraIssues.isRunning}} - No matches - {{/unless}} +
    - {{/unless}} - - <:item as |dd|> - -
    - -
    - {{dd.attrs.key}} - {{dd.attrs.summary}} -
    + {{/if}} +
    + + <:no-matches> + {{#unless (lt this.query.length 1)}} +
    + {{#unless this.searchJiraIssues.isRunning}} + No matches + {{/unless}} +
    + {{/unless}} + + <:item as |dd|> + +
    + +
    + {{dd.attrs.key}} + {{dd.attrs.summary}}
    - - - - {{/if}} -
    +
    + + +
    + {{/if}}
    -{{/unless}} +
    diff --git a/web/app/utils/ember-animated/animate-rotation.ts b/web/app/utils/ember-animated/animate-rotation.ts new file mode 100644 index 000000000..f5253f8b8 --- /dev/null +++ b/web/app/utils/ember-animated/animate-rotation.ts @@ -0,0 +1,55 @@ +// Modified from +// https://github.com/ember-animation/ember-animated/issues/38#issuecomment-392276685 + +import { Motion, rAF, Sprite, Tween } from "ember-animated"; +import { BaseOptions } from "ember-animated/-private/motion"; +import { TweenLike } from "ember-animated/-private/tween"; + +interface AnimateRotationOptions extends BaseOptions { + from?: number; + to?: number; + easing?: (time: number) => number; +} + +export class AnimatedRotation extends Motion { + prior: Motion | null | undefined = null; + tween: TweenLike | null = null; + + interrupted(motions: Motion[]): void { + this.prior = motions.find((m: Motion) => m instanceof this.constructor); + } + + *animate(): Generator, void> { + let { sprite, duration, opts } = this; + let to = + opts.to != null + ? opts.to + : sprite.finalComputedStyle != null + ? parseFloat(sprite.finalComputedStyle.opacity) + : 1; + let from; + + from = + opts.from != null + ? opts.from + : sprite.initialComputedStyle != null + ? parseFloat(sprite.initialComputedStyle.opacity) + : 0; + + this.tween = new Tween(from, to, duration); + + while (!this.tween.done) { + sprite.applyStyles({ + transform: `rotate(${this.tween.currentValue}deg)`, + }); + yield rAF(); + } + } +} + +export default function animateRotation( + sprite: Sprite, + opts?: Partial, +): Promise { + return new AnimatedRotation(sprite, opts).run(); +} From 44d9923a20e6d234d4517d47268f4a7dfd3734cb Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Thu, 21 Dec 2023 21:34:00 -0500 Subject: [PATCH 09/32] Animation progress --- web/app/components/project/index.hbs | 48 +++++++++++----------- web/app/components/project/index.ts | 60 ++++++++++++++-------------- 2 files changed, 55 insertions(+), 53 deletions(-) diff --git a/web/app/components/project/index.hbs b/web/app/components/project/index.hbs index f1c60abbd..25ba7357a 100644 --- a/web/app/components/project/index.hbs +++ b/web/app/components/project/index.hbs @@ -64,8 +64,8 @@
    + -
    {{! Title }} @@ -83,10 +83,10 @@ @isSaving={{this.titleIsSaving}} />
    - + {{#animated-if - (or this.projectIsActive this.description) + (or this.description this.projectIsActive) rules=this.descriptionTransitionRules }} {{! Description }} @@ -109,17 +109,20 @@ {{#if this.jiraIsEnabled}} {{! Jira }} -
    - {{#animated-if this.jiraWidgetIsShown rules=this.jiraTransitionRules}} - - {{/animated-if}} + {{! we like this padding whether the issue is shown... maybe find another approach }} + +
    + + {{#animated-if this.jiraWidgetIsShown rules=this.jiraTransitionRules}} + + {{/animated-if}} +
    {{/if}} @@ -139,17 +142,14 @@ initialInsertion=true finalRemoval=true }} -
    + - - - -
    + + {{/animated-if}} diff --git a/web/app/components/project/index.ts b/web/app/components/project/index.ts index 78e907e3b..c7f79571d 100644 --- a/web/app/components/project/index.ts +++ b/web/app/components/project/index.ts @@ -27,13 +27,13 @@ import { emptyTransition } from "hermes/utils/ember-animated/empty-transition"; import move from "ember-animated/motions/move"; import adjustCSS from "ember-animated/motions/adjust-css"; import { Resize } from "ember-animated/motions/resize"; -import { easeOutExpo } from "hermes/utils/ember-animated/easings"; +import { easeOutExpo, easeOutQuad } from "hermes/utils/ember-animated/easings"; import animateRotation from "hermes/utils/ember-animated/animate-rotation"; class ResizeProject extends Resize { *animate() { - this.opts.duration = Ember.testing ? 0 : 500; - // this.opts.easing = easeOutExpo; + this.opts.duration = Ember.testing ? 0 : 600; + this.opts.easing = easeOutQuad; yield* super.animate(); } } @@ -126,7 +126,7 @@ export default class ProjectIndexComponent extends Component Date: Fri, 22 Dec 2023 10:32:11 -0500 Subject: [PATCH 10/32] Improved animation logic --- web/app/components/project/index.hbs | 16 +------ web/app/components/project/index.ts | 62 ++++++++++++++++++++++++---- 2 files changed, 57 insertions(+), 21 deletions(-) diff --git a/web/app/components/project/index.hbs b/web/app/components/project/index.hbs index 25ba7357a..1b0bdd3e8 100644 --- a/web/app/components/project/index.hbs +++ b/web/app/components/project/index.hbs @@ -1,22 +1,10 @@ -
    - -
    -
    - {{! Placeholder until we have ProductAvatars }} - -
    -
    - +
    - - Status: - - diff --git a/web/app/components/project/index.ts b/web/app/components/project/index.ts index c7f79571d..85f7605f9 100644 --- a/web/app/components/project/index.ts +++ b/web/app/components/project/index.ts @@ -29,11 +29,13 @@ import adjustCSS from "ember-animated/motions/adjust-css"; import { Resize } from "ember-animated/motions/resize"; import { easeOutExpo, easeOutQuad } from "hermes/utils/ember-animated/easings"; import animateRotation from "hermes/utils/ember-animated/animate-rotation"; +import { empty } from "@ember/object/computed"; class ResizeProject extends Resize { *animate() { + console.log("resize"); this.opts.duration = Ember.testing ? 0 : 600; - this.opts.easing = easeOutQuad; + // this.opts.easing = easeOutQuad; yield* super.animate(); } } @@ -130,9 +132,18 @@ export default class ProjectIndexComponent extends Component Date: Fri, 22 Dec 2023 12:05:14 -0500 Subject: [PATCH 11/32] Start of improved PlusButton --- web/app/components/project/index.hbs | 10 +++++----- web/app/components/project/index.ts | 18 +++++++----------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/web/app/components/project/index.hbs b/web/app/components/project/index.hbs index 1b0bdd3e8..4f2dde462 100644 --- a/web/app/components/project/index.hbs +++ b/web/app/components/project/index.hbs @@ -52,7 +52,7 @@
    - +
    @@ -113,9 +113,6 @@
    {{/if}} -
    - -
    {{! Plus button }} @@ -141,6 +138,9 @@ {{/animated-if}} + + +
    {{! Resources }}
    diff --git a/web/app/components/project/index.ts b/web/app/components/project/index.ts index 85f7605f9..cb2de23c4 100644 --- a/web/app/components/project/index.ts +++ b/web/app/components/project/index.ts @@ -9,7 +9,7 @@ import { import { RelatedResourceSelector } from "hermes/components/related-resources"; import { inject as service } from "@ember/service"; import FetchService from "hermes/services/fetch"; -import { enqueueTask, restartableTask, task, timeout } from "ember-concurrency"; +import { enqueueTask, task, timeout } from "ember-concurrency"; import { HermesProject, JiraPickerResult } from "hermes/types/project"; import { ProjectStatus, @@ -21,15 +21,13 @@ import HermesFlashMessagesService from "hermes/services/flash-messages"; import { FLASH_MESSAGES_LONG_TIMEOUT } from "hermes/utils/ember-cli-flash/timeouts"; import updateRelatedResourcesSortOrder from "hermes/utils/update-related-resources-sort-order"; import Ember from "ember"; -import { TransitionContext, parallel, wait } from "ember-animated/."; +import { TransitionContext, wait } from "ember-animated/."; import { fadeIn, fadeOut } from "ember-animated/motions/opacity"; import { emptyTransition } from "hermes/utils/ember-animated/empty-transition"; import move from "ember-animated/motions/move"; -import adjustCSS from "ember-animated/motions/adjust-css"; import { Resize } from "ember-animated/motions/resize"; import { easeOutExpo, easeOutQuad } from "hermes/utils/ember-animated/easings"; import animateRotation from "hermes/utils/ember-animated/animate-rotation"; -import { empty } from "@ember/object/computed"; class ResizeProject extends Resize { *animate() { @@ -437,7 +435,7 @@ export default class ProjectIndexComponent extends Component Date: Fri, 22 Dec 2023 13:09:49 -0500 Subject: [PATCH 12/32] Smoother transition --- web/app/components/project/index.hbs | 150 ++++++++++++++------------- web/app/components/project/index.ts | 63 +++++------ 2 files changed, 108 insertions(+), 105 deletions(-) diff --git a/web/app/components/project/index.hbs b/web/app/components/project/index.hbs index 4f2dde462..eeb629db7 100644 --- a/web/app/components/project/index.hbs +++ b/web/app/components/project/index.hbs @@ -52,92 +52,94 @@
    - + +
    +
    + -
    - - {{! Title }} -
    - -
    - - - {{#animated-if - (or this.description this.projectIsActive) - rules=this.descriptionTransitionRules - }} - {{! Description }} -
    + {{! Title }} +
    - {{/animated-if}} - -
    + {{#animated-if + (or this.description this.projectIsActive) + rules=this.descriptionTransitionRules + }} + {{! Description }} +
    + +
    + {{/animated-if}} +
    - {{#if this.jiraIsEnabled}} - {{! Jira }} - {{! we like this padding whether the issue is shown... maybe find another approach }} +
    -
    - + + {{#if this.jiraIsEnabled}} + {{! Jira }} {{#animated-if this.jiraWidgetIsShown rules=this.jiraTransitionRules}} - +
    + +
    {{/animated-if}} -
    -
    - {{/if}} + {{/if}} +
    - {{! Plus button }} - - <:header as |rr|> - {{#animated-if - this.projectIsActive - rules=this.plusButtonTransitionRules - initialInsertion=true - finalRemoval=true - }} - + {{! Plus button }} + + <:header as |rr|> + {{#animated-if + this.projectIsActive + rules=this.plusButtonTransitionRules + initialInsertion=true + finalRemoval=true + }} + - - - {{/animated-if}} - - + + + {{/animated-if}} + + +
    diff --git a/web/app/components/project/index.ts b/web/app/components/project/index.ts index cb2de23c4..1884c0ea7 100644 --- a/web/app/components/project/index.ts +++ b/web/app/components/project/index.ts @@ -32,8 +32,8 @@ import animateRotation from "hermes/utils/ember-animated/animate-rotation"; class ResizeProject extends Resize { *animate() { console.log("resize"); - this.opts.duration = Ember.testing ? 0 : 600; - // this.opts.easing = easeOutQuad; + this.opts.duration = Ember.testing ? 0 : 1000; + this.opts.easing = easeOutExpo; yield* super.animate(); } } @@ -401,21 +401,39 @@ export default class ProjectIndexComponent extends Component Date: Fri, 22 Dec 2023 13:39:03 -0500 Subject: [PATCH 13/32] More smoothness --- web/app/components/project/index.hbs | 4 +- web/app/components/project/index.ts | 76 +++++++++++++--------------- 2 files changed, 36 insertions(+), 44 deletions(-) diff --git a/web/app/components/project/index.hbs b/web/app/components/project/index.hbs index eeb629db7..70c7de1e3 100644 --- a/web/app/components/project/index.hbs +++ b/web/app/components/project/index.hbs @@ -55,7 +55,7 @@
    - + {{! Title }}
    @@ -95,7 +95,7 @@
    - + {{#if this.jiraIsEnabled}} {{! Jira }} {{#animated-if this.jiraWidgetIsShown rules=this.jiraTransitionRules}} diff --git a/web/app/components/project/index.ts b/web/app/components/project/index.ts index 1884c0ea7..462b5db4c 100644 --- a/web/app/components/project/index.ts +++ b/web/app/components/project/index.ts @@ -29,10 +29,12 @@ import { Resize } from "ember-animated/motions/resize"; import { easeOutExpo, easeOutQuad } from "hermes/utils/ember-animated/easings"; import animateRotation from "hermes/utils/ember-animated/animate-rotation"; +const animationDuration = Ember.testing ? 0 : 450; + class ResizeProject extends Resize { *animate() { console.log("resize"); - this.opts.duration = Ember.testing ? 0 : 1000; + this.opts.duration = animationDuration; this.opts.easing = easeOutExpo; yield* super.animate(); } @@ -348,8 +350,6 @@ export default class ProjectIndexComponent extends Component Date: Fri, 22 Dec 2023 14:06:19 -0500 Subject: [PATCH 14/32] Update index.ts --- web/app/components/project/index.hbs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/web/app/components/project/index.hbs b/web/app/components/project/index.hbs index 70c7de1e3..70893be5a 100644 --- a/web/app/components/project/index.hbs +++ b/web/app/components/project/index.hbs @@ -54,7 +54,7 @@
    -
    +
    {{! Title }} @@ -95,8 +95,8 @@
    - - {{#if this.jiraIsEnabled}} + {{#if this.jiraIsEnabled}} + {{! Jira }} {{#animated-if this.jiraWidgetIsShown rules=this.jiraTransitionRules}}
    {{/animated-if}} - {{/if}} -
    +
    + {{/if}} {{! Plus button }} Date: Fri, 22 Dec 2023 14:08:08 -0500 Subject: [PATCH 15/32] Update index.hbs --- web/app/components/project/index.hbs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/web/app/components/project/index.hbs b/web/app/components/project/index.hbs index 70893be5a..f4b9e7878 100644 --- a/web/app/components/project/index.hbs +++ b/web/app/components/project/index.hbs @@ -96,13 +96,13 @@
    {{#if this.jiraIsEnabled}} - + {{! Jira }} {{#animated-if this.jiraWidgetIsShown rules=this.jiraTransitionRules}} -
    +
    Date: Fri, 22 Dec 2023 14:12:55 -0500 Subject: [PATCH 16/32] Update index.hbs --- web/app/components/project/index.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/components/project/index.hbs b/web/app/components/project/index.hbs index f4b9e7878..a70fd6bb2 100644 --- a/web/app/components/project/index.hbs +++ b/web/app/components/project/index.hbs @@ -54,7 +54,7 @@
    -
    +
    {{! Title }} From eb08a6a49d08afd61f28856f10e6cc6eddee6b0a Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Fri, 22 Dec 2023 15:31:03 -0500 Subject: [PATCH 17/32] Update project-test.ts --- .../acceptance/authenticated/projects/project-test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/web/tests/acceptance/authenticated/projects/project-test.ts b/web/tests/acceptance/authenticated/projects/project-test.ts index c4f88922f..ac9e485f1 100644 --- a/web/tests/acceptance/authenticated/projects/project-test.ts +++ b/web/tests/acceptance/authenticated/projects/project-test.ts @@ -573,25 +573,25 @@ module("Acceptance | authenticated/projects/project", function (hooks) { assert.dom(DOCUMENT_OVERFLOW_MENU_BUTTON).doesNotExist(); }); - test('the "add resource" button is disabled when the project is inactive', async function (this: AuthenticatedProjectsProjectRouteTestContext, assert) { + test('the "add resource" button is hidden when the project is inactive', async function (this: AuthenticatedProjectsProjectRouteTestContext, assert) { await visit("/projects/1"); - assert.dom(ADD_RESOURCE_BUTTON).isNotDisabled(); + assert.dom(ADD_RESOURCE_BUTTON).exists(); await click(STATUS_TOGGLE); await click(COMPLETED_STATUS_ACTION); - assert.dom(ADD_RESOURCE_BUTTON).isDisabled(); + assert.dom(ADD_RESOURCE_BUTTON).doesNotExist(); await click(STATUS_TOGGLE); await click(ARCHIVED_STATUS_ACTION); - assert.dom(ADD_RESOURCE_BUTTON).isDisabled(); + assert.dom(ADD_RESOURCE_BUTTON).doesNotExist(); await click(STATUS_TOGGLE); await click(ACTIVE_STATUS_ACTION); - assert.dom(ADD_RESOURCE_BUTTON).isNotDisabled(); + assert.dom(ADD_RESOURCE_BUTTON).exists(); }); test("you can't save an empty project title", async function (this: AuthenticatedProjectsProjectRouteTestContext, assert) { From ac7cf18b83f1c538995421fcd3aff75b00a02603 Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Tue, 2 Jan 2024 09:44:36 -0500 Subject: [PATCH 18/32] Update index.hbs --- web/app/components/project/index.hbs | 274 ++++++++++++++------------- 1 file changed, 145 insertions(+), 129 deletions(-) diff --git a/web/app/components/project/index.hbs b/web/app/components/project/index.hbs index a70fd6bb2..961110c62 100644 --- a/web/app/components/project/index.hbs +++ b/web/app/components/project/index.hbs @@ -1,148 +1,164 @@ -
    -
    -
    +
    - - <:anchor as |dd|> - - - - {{this.statusLabel}} - - - - <:item as |dd|> - - +
    +
    + + <:anchor as |dd|> + + + + {{this.statusLabel}} + + + + <:item as |dd|> + - <:default> -
    - - - {{dd.attrs.label}} - -
    - - -
    - -
    + + <:default> +
    + + + {{dd.attrs.label}} + +
    + +
    + + + +
    +
    - - + {{! Big icon }} +
    + +
    -
    -
    - -
    -
    - - {{! Title }} -
    - -
    + +
    +
    + - {{#animated-if - (or this.description this.projectIsActive) - rules=this.descriptionTransitionRules - }} - {{! Description }} -
    + {{! Title }} +
    - {{/animated-if}} - -
    + {{#animated-if + (or this.description this.projectIsActive) + rules=this.descriptionTransitionRules + }} + {{! Description }} +
    + +
    + {{/animated-if}} +
    - {{#if this.jiraIsEnabled}} - - {{! Jira }} - {{#animated-if this.jiraWidgetIsShown rules=this.jiraTransitionRules}} -
    - -
    - {{/animated-if}} -
    - {{/if}} +
    - {{! Plus button }} - - <:header as |rr|> - {{#animated-if - this.projectIsActive - rules=this.plusButtonTransitionRules - initialInsertion=true - finalRemoval=true - }} - + {{#if this.jiraIsEnabled}} + + {{! Jira }} + {{#animated-if this.jiraWidgetIsShown rules=this.jiraTransitionRules}} +
    + +
    + {{/animated-if}} +
    + {{/if}} - -
    - {{/animated-if}} - -
    -
    -
    + {{! Plus button }} + + <:header as |rr|> + {{#animated-if + this.projectIsActive + rules=this.plusButtonTransitionRules + initialInsertion=true + finalRemoval=true + }} + + + + + {{/animated-if}} + + +
    + +
    -
    +
    {{! Resources }}
    From 7c401028dbddeb50a21b5dc07cadac6448b6e9dd Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Wed, 3 Jan 2024 10:03:01 -0500 Subject: [PATCH 19/32] Design tweaks --- web/app/components/header/nav.hbs | 2 +- web/app/components/project/index.hbs | 62 ++++++++++++++-------------- web/app/components/project/index.ts | 31 +++++++++++++- web/app/utils/time-ago.ts | 2 +- 4 files changed, 64 insertions(+), 33 deletions(-) diff --git a/web/app/components/header/nav.hbs b/web/app/components/header/nav.hbs index a293ee00a..3cf871a7f 100644 --- a/web/app/components/header/nav.hbs +++ b/web/app/components/header/nav.hbs @@ -8,7 +8,7 @@ Projects diff --git a/web/app/components/project/index.hbs b/web/app/components/project/index.hbs index 961110c62..193ee734c 100644 --- a/web/app/components/project/index.hbs +++ b/web/app/components/project/index.hbs @@ -1,8 +1,28 @@
    -
    -
    +
    +
    + + + + {{!
    }} + Project + {{#if this.modifiedTime}} + | + + Saved + {{log "TEMPLATE" this.modifiedTime}} + {{time-ago this.modifiedTime}} + + {{/if}} +
    +
    + Status: +
    -
    - {{! Big icon }} -
    - -
    -
    @@ -115,7 +122,7 @@ > {{! Jira }} {{#animated-if this.jiraWidgetIsShown rules=this.jiraTransitionRules}} -
    +
    Documents - + /> --}}
      {{#each this.hermesDocuments as |document|}} @@ -204,10 +211,10 @@

      External links

      - + /> --}}
      {{#each this.externalLinks as |link i|}} @@ -278,11 +285,6 @@ by {{@project.creator}} - | - - Last modified - {{time-ago @project.modifiedTime}} -
    diff --git a/web/app/components/project/index.ts b/web/app/components/project/index.ts index 462b5db4c..d0c619aab 100644 --- a/web/app/components/project/index.ts +++ b/web/app/components/project/index.ts @@ -9,7 +9,7 @@ import { import { RelatedResourceSelector } from "hermes/components/related-resources"; import { inject as service } from "@ember/service"; import FetchService from "hermes/services/fetch"; -import { enqueueTask, task, timeout } from "ember-concurrency"; +import { enqueueTask, restartableTask, task, timeout } from "ember-concurrency"; import { HermesProject, JiraPickerResult } from "hermes/types/project"; import { ProjectStatus, @@ -30,6 +30,7 @@ import { easeOutExpo, easeOutQuad } from "hermes/utils/ember-animated/easings"; import animateRotation from "hermes/utils/ember-animated/animate-rotation"; const animationDuration = Ember.testing ? 0 : 450; +const DEFAULT_REFETCH_DELAY = 1250; class ResizeProject extends Resize { *animate() { @@ -67,6 +68,7 @@ export default class ProjectIndexComponent extends Component { + this.refetchModifiedTimeDelay = DEFAULT_REFETCH_DELAY; + this.modifiedTime = Date.now() / 1000; + void this.updateModifiedTime.perform(); + }); + + private updateModifiedTime = restartableTask(async () => { + this.timesFetched += 1; + + this.modifiedTime = + this.modifiedTime - this.refetchModifiedTimeDelay / 10000; + + if (this.timesFetched > 15) { + this.refetchModifiedTimeDelay += DEFAULT_REFETCH_DELAY * 4; + } + + await timeout(this.refetchModifiedTimeDelay); + + void this.updateModifiedTime.perform(); + }); + /** * The action to save basic project attributes, * such as title, description, and status. @@ -495,7 +521,10 @@ export default class ProjectIndexComponent extends Component Date: Wed, 3 Jan 2024 11:33:46 -0500 Subject: [PATCH 20/32] Better saving/saved states --- web/app/components/project/index.hbs | 36 ++++++++++---- web/app/components/project/index.ts | 73 +++++++++++++++++++--------- web/app/styles/animations.scss | 32 +++++++++++- web/app/utils/time-ago.ts | 2 +- 4 files changed, 109 insertions(+), 34 deletions(-) diff --git a/web/app/components/project/index.hbs b/web/app/components/project/index.hbs index 193ee734c..01b5592d1 100644 --- a/web/app/components/project/index.hbs +++ b/web/app/components/project/index.hbs @@ -11,17 +11,35 @@ {{!
    }} - Project - {{#if this.modifiedTime}} - | - - Saved - {{log "TEMPLATE" this.modifiedTime}} - {{time-ago this.modifiedTime}} - + Project + {{#if this.projectSavedMessageIsShown}} +
    +
    +
    + + {{if this.projectIsSaving "Saving..." "Saved!"}} +
    +
    {{/if}}
    -
    +
    Status: { - this.refetchModifiedTimeDelay = DEFAULT_REFETCH_DELAY; - this.modifiedTime = Date.now() / 1000; - void this.updateModifiedTime.perform(); - }); - - private updateModifiedTime = restartableTask(async () => { - this.timesFetched += 1; - - this.modifiedTime = - this.modifiedTime - this.refetchModifiedTimeDelay / 10000; + /** + * The task to hide the "Saved" message after a delay. + * Called when the save task completes. + */ + private hideSavedMessageAfterDelay = restartableTask(async () => { + await timeout(Ember.testing ? 0 : 2000); - if (this.timesFetched > 15) { - this.refetchModifiedTimeDelay += DEFAULT_REFETCH_DELAY * 4; - } + assert( + "projectSavedMessageElement must exist", + this.projectSavedMessageElement, + ); - await timeout(this.refetchModifiedTimeDelay); + this.projectSavedMessageElement.classList.add("fade-out-forwards"); + this.projectSavedMessageElement + .querySelector(".saved-message") + ?.classList.add("slide-to-left-forwards"); + await timeout(Ember.testing ? 0 : 500); - void this.updateModifiedTime.perform(); + this.projectSavedMessageIsShown = false; }); /** @@ -522,14 +544,17 @@ export default class ProjectIndexComponent extends Component Date: Wed, 3 Jan 2024 12:26:05 -0500 Subject: [PATCH 21/32] Remove saved message --- web/app/components/project/index.hbs | 29 +-------------------- web/app/components/project/index.ts | 39 +++------------------------- 2 files changed, 4 insertions(+), 64 deletions(-) diff --git a/web/app/components/project/index.hbs b/web/app/components/project/index.hbs index 01b5592d1..ee25d3c51 100644 --- a/web/app/components/project/index.hbs +++ b/web/app/components/project/index.hbs @@ -10,34 +10,7 @@ > - {{!
    }} - Project - {{#if this.projectSavedMessageIsShown}} -
    -
    -
    - - {{if this.projectIsSaving "Saving..." "Saved!"}} -
    -
    - {{/if}} +
    Project
    Status: diff --git a/web/app/components/project/index.ts b/web/app/components/project/index.ts index 3e9876590..5edb034ea 100644 --- a/web/app/components/project/index.ts +++ b/web/app/components/project/index.ts @@ -78,13 +78,6 @@ export default class ProjectIndexComponent extends Component { - await timeout(Ember.testing ? 0 : 2000); - - assert( - "projectSavedMessageElement must exist", - this.projectSavedMessageElement, - ); - - this.projectSavedMessageElement.classList.add("fade-out-forwards"); - this.projectSavedMessageElement - .querySelector(".saved-message") - ?.classList.add("slide-to-left-forwards"); - await timeout(Ember.testing ? 0 : 500); - - this.projectSavedMessageIsShown = false; - }); - /** * The action to save basic project attributes, * such as title, description, and status. @@ -544,17 +516,12 @@ export default class ProjectIndexComponent extends Component Date: Wed, 3 Jan 2024 13:13:10 -0500 Subject: [PATCH 22/32] Add counts back --- web/app/components/project/index.hbs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/app/components/project/index.hbs b/web/app/components/project/index.hbs index ee25d3c51..ecb7e709e 100644 --- a/web/app/components/project/index.hbs +++ b/web/app/components/project/index.hbs @@ -168,10 +168,10 @@

    Documents

    - {{!-- --}} + />
      {{#each this.hermesDocuments as |document|}} @@ -202,10 +202,10 @@

      External links

      - {{!-- --}} + />
      {{#each this.externalLinks as |link i|}} From b3a73d0ea7571a129b6d5a865f0986ac9b7b8298 Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Wed, 3 Jan 2024 13:35:08 -0500 Subject: [PATCH 23/32] Cleanup --- web/app/components/doc/tile-medium.hbs | 6 +- web/app/components/project/index.hbs | 6 ++ web/app/components/project/index.ts | 6 +- .../utils/ember-animated/animate-rotation.ts | 55 ------------------- 4 files changed, 11 insertions(+), 62 deletions(-) delete mode 100644 web/app/utils/ember-animated/animate-rotation.ts diff --git a/web/app/components/doc/tile-medium.hbs b/web/app/components/doc/tile-medium.hbs index 62343991e..3fb29884e 100644 --- a/web/app/components/doc/tile-medium.hbs +++ b/web/app/components/doc/tile-medium.hbs @@ -66,7 +66,7 @@ @route="authenticated.documents" @query={{hash owners=(array (get @doc.owners 0))}} disabled={{not (get @doc.owners 0)}} - class="underlined-link" + class="underline-on-hover" > {{or (get @doc.owners 0) "Unknown"}} @@ -77,7 +77,7 @@ <:default> {{@doc.product}} @@ -87,7 +87,7 @@ | {{! Created time }} - + {{time-ago @doc.createdTime}}
    diff --git a/web/app/components/project/index.hbs b/web/app/components/project/index.hbs index ecb7e709e..e8d610021 100644 --- a/web/app/components/project/index.hbs +++ b/web/app/components/project/index.hbs @@ -5,6 +5,7 @@
    @@ -276,6 +277,11 @@ by {{@project.creator}} + | + + Last modified + {{time-ago @project.modifiedTime}} +
    diff --git a/web/app/components/project/index.ts b/web/app/components/project/index.ts index 5edb034ea..666740f86 100644 --- a/web/app/components/project/index.ts +++ b/web/app/components/project/index.ts @@ -9,7 +9,7 @@ import { import { RelatedResourceSelector } from "hermes/components/related-resources"; import { inject as service } from "@ember/service"; import FetchService from "hermes/services/fetch"; -import { enqueueTask, restartableTask, task, timeout } from "ember-concurrency"; +import { enqueueTask, task, timeout } from "ember-concurrency"; import { HermesProject, JiraPickerResult } from "hermes/types/project"; import { ProjectStatus, @@ -26,11 +26,9 @@ import { fadeIn, fadeOut } from "ember-animated/motions/opacity"; import { emptyTransition } from "hermes/utils/ember-animated/empty-transition"; import move from "ember-animated/motions/move"; import { Resize } from "ember-animated/motions/resize"; -import { easeOutExpo, easeOutQuad } from "hermes/utils/ember-animated/easings"; -import animateRotation from "hermes/utils/ember-animated/animate-rotation"; +import { easeOutExpo } from "hermes/utils/ember-animated/easings"; const animationDuration = Ember.testing ? 0 : 450; -const DEFAULT_REFETCH_DELAY = 1000; class ResizeProject extends Resize { *animate() { diff --git a/web/app/utils/ember-animated/animate-rotation.ts b/web/app/utils/ember-animated/animate-rotation.ts deleted file mode 100644 index f5253f8b8..000000000 --- a/web/app/utils/ember-animated/animate-rotation.ts +++ /dev/null @@ -1,55 +0,0 @@ -// Modified from -// https://github.com/ember-animation/ember-animated/issues/38#issuecomment-392276685 - -import { Motion, rAF, Sprite, Tween } from "ember-animated"; -import { BaseOptions } from "ember-animated/-private/motion"; -import { TweenLike } from "ember-animated/-private/tween"; - -interface AnimateRotationOptions extends BaseOptions { - from?: number; - to?: number; - easing?: (time: number) => number; -} - -export class AnimatedRotation extends Motion { - prior: Motion | null | undefined = null; - tween: TweenLike | null = null; - - interrupted(motions: Motion[]): void { - this.prior = motions.find((m: Motion) => m instanceof this.constructor); - } - - *animate(): Generator, void> { - let { sprite, duration, opts } = this; - let to = - opts.to != null - ? opts.to - : sprite.finalComputedStyle != null - ? parseFloat(sprite.finalComputedStyle.opacity) - : 1; - let from; - - from = - opts.from != null - ? opts.from - : sprite.initialComputedStyle != null - ? parseFloat(sprite.initialComputedStyle.opacity) - : 0; - - this.tween = new Tween(from, to, duration); - - while (!this.tween.done) { - sprite.applyStyles({ - transform: `rotate(${this.tween.currentValue}deg)`, - }); - yield rAF(); - } - } -} - -export default function animateRotation( - sprite: Sprite, - opts?: Partial, -): Promise { - return new AnimatedRotation(sprite, opts).run(); -} From b0169502fb1d9e7975fe4504653ead562151add6 Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Wed, 3 Jan 2024 13:42:28 -0500 Subject: [PATCH 24/32] Reduce diff --- web/app/components/doc/tile-medium.hbs | 2 +- web/app/components/project/index.hbs | 1 - web/app/components/project/index.ts | 17 +++----------- web/app/styles/animations.scss | 32 +------------------------- 4 files changed, 5 insertions(+), 47 deletions(-) diff --git a/web/app/components/doc/tile-medium.hbs b/web/app/components/doc/tile-medium.hbs index 3fb29884e..6a34d07db 100644 --- a/web/app/components/doc/tile-medium.hbs +++ b/web/app/components/doc/tile-medium.hbs @@ -87,7 +87,7 @@ | {{! Created time }} - + {{time-ago @doc.createdTime}}
    diff --git a/web/app/components/project/index.hbs b/web/app/components/project/index.hbs index e8d610021..839782a03 100644 --- a/web/app/components/project/index.hbs +++ b/web/app/components/project/index.hbs @@ -41,7 +41,6 @@ <:default>
    diff --git a/web/app/components/project/index.ts b/web/app/components/project/index.ts index 666740f86..d1a1480ee 100644 --- a/web/app/components/project/index.ts +++ b/web/app/components/project/index.ts @@ -32,7 +32,6 @@ const animationDuration = Ember.testing ? 0 : 450; class ResizeProject extends Resize { *animate() { - console.log("resize"); this.opts.duration = animationDuration; this.opts.easing = easeOutExpo; yield* super.animate(); @@ -397,15 +396,7 @@ export default class ProjectIndexComponent extends Component Date: Wed, 3 Jan 2024 13:44:26 -0500 Subject: [PATCH 25/32] Update index.ts --- web/app/components/project/index.ts | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/web/app/components/project/index.ts b/web/app/components/project/index.ts index d1a1480ee..dcd904caa 100644 --- a/web/app/components/project/index.ts +++ b/web/app/components/project/index.ts @@ -75,12 +75,6 @@ export default class ProjectIndexComponent extends Component Date: Thu, 4 Jan 2024 13:46:37 -0500 Subject: [PATCH 26/32] Animated transform --- web/app/components/project/index.hbs | 16 +- web/app/components/project/index.ts | 44 +++-- web/app/components/x/dropdown-list/item.hbs | 1 + web/app/components/x/dropdown-list/item.ts | 18 +- .../components/x/dropdown-list/link-to.hbs | 2 +- web/app/components/x/dropdown-list/link-to.ts | 18 +- .../utils/ember-animated/animate-transform.ts | 157 ++++++++++++++++++ 7 files changed, 226 insertions(+), 30 deletions(-) create mode 100644 web/app/utils/ember-animated/animate-transform.ts diff --git a/web/app/components/project/index.hbs b/web/app/components/project/index.hbs index 839782a03..9668452af 100644 --- a/web/app/components/project/index.hbs +++ b/web/app/components/project/index.hbs @@ -17,8 +17,9 @@ Status: <:anchor as |dd|> @@ -34,10 +35,7 @@ <:item as |dd|> - + - - + {{/animated-if}} +
    @@ -157,7 +155,6 @@
    - {{! Resources }}
    {{#if (or this.hermesDocuments.length this.externalLinks.length)}} @@ -258,6 +255,7 @@ data-test-empty-body class="mt-7 flex h-48 flex-col items-start justify-center" > + {{! TODO: this doesn't make sense for completed/archived projects }}
    Nothing here yet
    diff --git a/web/app/components/project/index.ts b/web/app/components/project/index.ts index dcd904caa..81144cd82 100644 --- a/web/app/components/project/index.ts +++ b/web/app/components/project/index.ts @@ -26,7 +26,8 @@ import { fadeIn, fadeOut } from "ember-animated/motions/opacity"; import { emptyTransition } from "hermes/utils/ember-animated/empty-transition"; import move from "ember-animated/motions/move"; import { Resize } from "ember-animated/motions/resize"; -import { easeOutExpo } from "hermes/utils/ember-animated/easings"; +import { easeOutExpo, easeOutQuad } from "hermes/utils/ember-animated/easings"; +import animateTransform from "hermes/utils/ember-animated/animate-transform"; const animationDuration = Ember.testing ? 0 : 450; @@ -428,23 +429,46 @@ export default class ProjectIndexComponent extends Component { - this.args.hideContent(); - }); - } else { - next(() => { - this.args.hideContent(); - }); - } + this.args.hideContent(); + // if (Ember.testing) { + // } else { + // // this is only helpful for linkTos.... we need to scope appropriately + + // next(() => { + // this.args.hideContent(); + // }); + // } } /** diff --git a/web/app/components/x/dropdown-list/link-to.hbs b/web/app/components/x/dropdown-list/link-to.hbs index 85dc93704..5fbbe9228 100644 --- a/web/app/components/x/dropdown-list/link-to.hbs +++ b/web/app/components/x/dropdown-list/link-to.hbs @@ -2,7 +2,7 @@ data-test-x-dropdown-list-item-link-to {{did-insert @registerElement}} {{on "mouseenter" @focusMouseTarget}} - {{on "click" @onClick}} + {{on "click" this.onClick}} role={{@role}} aria-selected={{@isAriaSelected}} tabindex="-1" diff --git a/web/app/components/x/dropdown-list/link-to.ts b/web/app/components/x/dropdown-list/link-to.ts index d634acb1f..039ccdc3e 100644 --- a/web/app/components/x/dropdown-list/link-to.ts +++ b/web/app/components/x/dropdown-list/link-to.ts @@ -1,5 +1,8 @@ import Component from "@glimmer/component"; import { XDropdownListInteractiveComponentArgs } from "./_shared"; +import { action } from "@ember/object"; +import Ember from "ember"; +import { next, schedule } from "@ember/runloop"; interface XDropdownListLinkToComponentSignature { Element: HTMLAnchorElement; @@ -8,13 +11,26 @@ interface XDropdownListLinkToComponentSignature { query?: Record; model?: unknown; models?: unknown[]; + hideContent: () => void; }; Blocks: { default: []; }; } -export default class XDropdownListLinkToComponent extends Component {} +export default class XDropdownListLinkToComponent extends Component { + @action onClick(): void { + if (Ember.testing) { + schedule("afterRender", () => { + this.args.hideContent(); + }); + } else { + next(() => { + this.args.hideContent(); + }); + } + } +} declare module "@glint/environment-ember-loose/registry" { export default interface Registry { diff --git a/web/app/utils/ember-animated/animate-transform.ts b/web/app/utils/ember-animated/animate-transform.ts new file mode 100644 index 000000000..11f8861a6 --- /dev/null +++ b/web/app/utils/ember-animated/animate-transform.ts @@ -0,0 +1,157 @@ +/** + * A transition to control all transform properties at once. + * This is a work in progress and limited in functionality. + * Modified from the `move` and `opacity` motions + */ + +import { assert } from "@ember/debug"; +import { Motion, rAF, Sprite, Tween } from "ember-animated"; +import { BaseOptions } from "ember-animated/-private/motion"; +import { TweenLike } from "ember-animated/-private/tween"; + +interface AnimateTransformOptions extends BaseOptions { + scale?: { + from?: number; + to?: number; + duration?: number; + }; + rotate?: { + from?: number; + to?: number; + duration?: number; + }; + x?: { + from?: number; + to?: number; + duration?: number; + }; + y?: { + from?: number; + to?: number; + duration?: number; + }; + easing?: (time: number) => number; +} + +export class AnimatedTransform extends Motion { + prior: Motion | null | undefined = null; + tween: TweenLike | null = null; + + interrupted(motions: Motion[]): void { + this.prior = motions.find((m: Motion) => m instanceof this.constructor); + } + + *animate(): Generator, void> { + let { sprite, duration, opts } = this; + + let xTo = opts.x?.to; + let xFrom = opts.x?.from; + + let yTo = opts.y?.to; + let yFrom = opts.y?.from; + + let rotateTo = opts.rotate?.to; + let rotateFrom = opts.rotate?.from; + + let scaleTo = opts.scale?.to; + let scaleFrom = opts.scale?.from; + + let xTween: TweenLike | null = null; + let yTween: TweenLike | null = null; + let rotateTween: TweenLike | null = null; + let scaleTween: TweenLike | null = null; + + if (opts.x) { + xTween = new Tween( + xFrom ?? 0, + xTo ?? 0, + opts.x.duration ?? duration, + opts.easing, + ); + } + + if (opts.y) { + yTween = new Tween( + yFrom ?? 0, + yTo ?? 0, + opts.y.duration ?? duration, + opts.easing, + ); + } + + if (opts.rotate) { + rotateTween = new Tween( + rotateFrom ?? + (sprite.initialComputedStyle != null + ? parseFloat(sprite.initialComputedStyle.opacity) + : 0), + rotateTo ?? + (sprite.finalComputedStyle != null + ? parseFloat(sprite.finalComputedStyle.opacity) + : 1), + opts.rotate.duration ?? duration, + opts.easing, + ); + } + + if (opts.scale) { + scaleTween = new Tween( + scaleFrom ?? 1, + scaleTo ?? 1, + opts.scale?.duration ?? duration, + opts.easing, + ); + } + + let tweenIsDone = () => { + return ( + (xTween == null || xTween.done) && + (yTween == null || yTween.done) && + (rotateTween == null || rotateTween.done) && + (scaleTween == null || scaleTween.done) + ); + }; + + while (!tweenIsDone()) { + const shouldApplyX = xTween != null && !xTween.done; + const shouldApplyY = yTween != null && !yTween.done; + const shouldApplyRotate = rotateTween != null && !rotateTween.done; + const shouldApplyScale = scaleTween != null && !scaleTween.done; + + let transformString = ""; + + if (shouldApplyX) { + assert("xTween must exist", xTween); + transformString += `translateX(${xTween.currentValue}px) `; + } + + if (shouldApplyY) { + assert("yTween must exist", yTween); + transformString += `translateY(${yTween.currentValue}px) `; + } + + if (shouldApplyRotate) { + assert("rotateTween must exist", rotateTween); + transformString += `rotate(${rotateTween.currentValue}deg) `; + } + + if (shouldApplyScale) { + assert("scaleTween must exist", scaleTween); + transformString += `scale(${scaleTween.currentValue}) `; + } + + sprite.applyStyles({ + transform: transformString, + }); + + yield rAF(); + } + } +} + +export default function animateTransform( + sprite: Sprite, + opts?: Partial, +): Promise { + return new AnimatedTransform(sprite, opts).run(); +} From b982c3b137ded8e9f54bed8b5b08d4f1bd6ab0be Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Thu, 4 Jan 2024 14:10:04 -0500 Subject: [PATCH 27/32] Cleanup --- web/app/components/project/index.ts | 16 +-- .../utils/ember-animated/animate-transform.ts | 102 ++++++++---------- 2 files changed, 56 insertions(+), 62 deletions(-) diff --git a/web/app/components/project/index.ts b/web/app/components/project/index.ts index 81144cd82..4a8049208 100644 --- a/web/app/components/project/index.ts +++ b/web/app/components/project/index.ts @@ -398,6 +398,7 @@ export default class ProjectIndexComponent extends Component number; @@ -44,42 +40,41 @@ export class AnimatedTransform extends Motion { *animate(): Generator, void> { let { sprite, duration, opts } = this; - let xTo = opts.x?.to; - let xFrom = opts.x?.from; + let { translate, rotate, scale, easing } = opts; - let yTo = opts.y?.to; - let yFrom = opts.y?.from; + let xTo = translate?.xTo; + let xFrom = translate?.xFrom; - let rotateTo = opts.rotate?.to; - let rotateFrom = opts.rotate?.from; + let yTo = translate?.yTo; + let yFrom = translate?.yFrom; - let scaleTo = opts.scale?.to; - let scaleFrom = opts.scale?.from; + let rotateTo = rotate?.to; + let rotateFrom = rotate?.from; - let xTween: TweenLike | null = null; - let yTween: TweenLike | null = null; + let scaleTo = scale?.to; + let scaleFrom = scale?.from; + + let translateXTween: TweenLike | null = null; + let translateYTween: TweenLike | null = null; let rotateTween: TweenLike | null = null; let scaleTween: TweenLike | null = null; - if (opts.x) { - xTween = new Tween( + if (translate) { + translateXTween = new Tween( xFrom ?? 0, xTo ?? 0, - opts.x.duration ?? duration, + translate.duration ?? duration, opts.easing, ); - } - - if (opts.y) { - yTween = new Tween( + translateYTween = new Tween( yFrom ?? 0, yTo ?? 0, - opts.y.duration ?? duration, + translate.duration ?? duration, opts.easing, ); } - if (opts.rotate) { + if (rotate) { rotateTween = new Tween( rotateFrom ?? (sprite.initialComputedStyle != null @@ -89,55 +84,50 @@ export class AnimatedTransform extends Motion { (sprite.finalComputedStyle != null ? parseFloat(sprite.finalComputedStyle.opacity) : 1), - opts.rotate.duration ?? duration, - opts.easing, + rotate.duration ?? duration, + easing, ); } - if (opts.scale) { + if (scale) { scaleTween = new Tween( scaleFrom ?? 1, scaleTo ?? 1, - opts.scale?.duration ?? duration, - opts.easing, + scale?.duration ?? duration, + easing, ); } + const shouldApplyTranslate = + (translateXTween != null && !translateXTween.done) || + (translateYTween != null && !translateYTween.done); + const shouldApplyRotate = rotateTween != null && !rotateTween.done; + const shouldApplyScale = scaleTween != null && !scaleTween.done; + let tweenIsDone = () => { return ( - (xTween == null || xTween.done) && - (yTween == null || yTween.done) && - (rotateTween == null || rotateTween.done) && - (scaleTween == null || scaleTween.done) + (!shouldApplyTranslate || + (translateXTween?.done && translateYTween?.done)) && + (!shouldApplyRotate || rotateTween?.done) && + (!shouldApplyScale || scaleTween?.done) ); }; while (!tweenIsDone()) { - const shouldApplyX = xTween != null && !xTween.done; - const shouldApplyY = yTween != null && !yTween.done; - const shouldApplyRotate = rotateTween != null && !rotateTween.done; - const shouldApplyScale = scaleTween != null && !scaleTween.done; - let transformString = ""; - if (shouldApplyX) { - assert("xTween must exist", xTween); - transformString += `translateX(${xTween.currentValue}px) `; - } - - if (shouldApplyY) { - assert("yTween must exist", yTween); - transformString += `translateY(${yTween.currentValue}px) `; + if (shouldApplyTranslate) { + transformString += `translate(${ + translateXTween?.currentValue ?? 0 + }px, ${translateYTween?.currentValue ?? 0}px) `; } if (shouldApplyRotate) { - assert("rotateTween must exist", rotateTween); - transformString += `rotate(${rotateTween.currentValue}deg) `; + transformString += `rotate(${rotateTween?.currentValue ?? 0}deg) `; } if (shouldApplyScale) { - assert("scaleTween must exist", scaleTween); - transformString += `scale(${scaleTween.currentValue}) `; + transformString += `scale(${scaleTween?.currentValue ?? 1}) `; } sprite.applyStyles({ From 97bd3a100c82b3e73a76c7285045936e34d0110a Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Thu, 4 Jan 2024 14:20:18 -0500 Subject: [PATCH 28/32] Rename some variables --- .../utils/ember-animated/animate-transform.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/web/app/utils/ember-animated/animate-transform.ts b/web/app/utils/ember-animated/animate-transform.ts index 33b766246..629242d91 100644 --- a/web/app/utils/ember-animated/animate-transform.ts +++ b/web/app/utils/ember-animated/animate-transform.ts @@ -39,14 +39,13 @@ export class AnimatedTransform extends Motion { *animate(): Generator, void> { let { sprite, duration, opts } = this; - let { translate, rotate, scale, easing } = opts; - let xTo = translate?.xTo; - let xFrom = translate?.xFrom; + let translateXTo = translate?.xTo; + let translateXFrom = translate?.xFrom; - let yTo = translate?.yTo; - let yFrom = translate?.yFrom; + let translateYTo = translate?.yTo; + let translateYFrom = translate?.yFrom; let rotateTo = rotate?.to; let rotateFrom = rotate?.from; @@ -61,14 +60,14 @@ export class AnimatedTransform extends Motion { if (translate) { translateXTween = new Tween( - xFrom ?? 0, - xTo ?? 0, + translateXFrom ?? 0, + translateXTo ?? 0, translate.duration ?? duration, opts.easing, ); translateYTween = new Tween( - yFrom ?? 0, - yTo ?? 0, + translateYFrom ?? 0, + translateYTo ?? 0, translate.duration ?? duration, opts.easing, ); @@ -101,6 +100,7 @@ export class AnimatedTransform extends Motion { const shouldApplyTranslate = (translateXTween != null && !translateXTween.done) || (translateYTween != null && !translateYTween.done); + const shouldApplyRotate = rotateTween != null && !rotateTween.done; const shouldApplyScale = scaleTween != null && !scaleTween.done; From 98ae964d2818206ed5b0fd8c32e5802eddf41013 Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Thu, 4 Jan 2024 14:33:41 -0500 Subject: [PATCH 29/32] Move hide-dropdown handling to LinkTo --- web/app/components/x/dropdown-list/item.hbs | 1 + web/app/components/x/dropdown-list/item.ts | 19 +--------------- .../components/x/dropdown-list/link-to.hbs | 2 +- web/app/components/x/dropdown-list/link-to.ts | 22 ++++++++++++++++++- 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/web/app/components/x/dropdown-list/item.hbs b/web/app/components/x/dropdown-list/item.hbs index 90bf58135..0a1a34de7 100644 --- a/web/app/components/x/dropdown-list/item.hbs +++ b/web/app/components/x/dropdown-list/item.hbs @@ -55,6 +55,7 @@ registerElement=this.registerElement focusMouseTarget=(perform this.maybeFocusMouseTarget) onClick=this.onClick + hideContent=@hideContent ) ExternalLink=(component "x/dropdown-list/external-link" diff --git a/web/app/components/x/dropdown-list/item.ts b/web/app/components/x/dropdown-list/item.ts index 9c298e908..dec0c12ac 100644 --- a/web/app/components/x/dropdown-list/item.ts +++ b/web/app/components/x/dropdown-list/item.ts @@ -129,24 +129,7 @@ export default class XDropdownListItemComponent extends Component handling. - * - * This approach causes issues when testing, so we - * use `schedule` as an approximation. - * - * TODO: Improve this. - */ - if (Ember.testing) { - schedule("afterRender", () => { - this.args.hideContent(); - }); - } else { - next(() => { - this.args.hideContent(); - }); - } + this.args.hideContent(); } /** diff --git a/web/app/components/x/dropdown-list/link-to.hbs b/web/app/components/x/dropdown-list/link-to.hbs index 85dc93704..eed9d6036 100644 --- a/web/app/components/x/dropdown-list/link-to.hbs +++ b/web/app/components/x/dropdown-list/link-to.hbs @@ -2,7 +2,7 @@ data-test-x-dropdown-list-item-link-to {{did-insert @registerElement}} {{on "mouseenter" @focusMouseTarget}} - {{on "click" @onClick}} + {{on "click" this.hideDropdown}} role={{@role}} aria-selected={{@isAriaSelected}} tabindex="-1" diff --git a/web/app/components/x/dropdown-list/link-to.ts b/web/app/components/x/dropdown-list/link-to.ts index d634acb1f..1386de9a2 100644 --- a/web/app/components/x/dropdown-list/link-to.ts +++ b/web/app/components/x/dropdown-list/link-to.ts @@ -1,5 +1,8 @@ import Component from "@glimmer/component"; import { XDropdownListInteractiveComponentArgs } from "./_shared"; +import { action } from "@ember/object"; +import Ember from "ember"; +import { next, schedule } from "@ember/runloop"; interface XDropdownListLinkToComponentSignature { Element: HTMLAnchorElement; @@ -8,13 +11,30 @@ interface XDropdownListLinkToComponentSignature { query?: Record; model?: unknown; models?: unknown[]; + hideContent: () => void; }; Blocks: { default: []; }; } -export default class XDropdownListLinkToComponent extends Component {} +export default class XDropdownListLinkToComponent extends Component { + /** + * The action to close the dropdown. Called on click. + * We wait until the next run loop so that we don't interfere with + * Ember's handling. Because this approach causes issues + * when testing, we use `schedule` as an approximation. + */ + @action protected hideDropdown(): void { + if (Ember.testing) { + schedule("afterRender", this.args.hideContent); + } else { + next(() => { + this.args.hideContent(); + }); + } + } +} declare module "@glint/environment-ember-loose/registry" { export default interface Registry { From f6986e671c1e32894e9ef40d321f302a5cf65dc7 Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Thu, 4 Jan 2024 14:48:19 -0500 Subject: [PATCH 30/32] Fix action, improve docs --- web/app/components/x/dropdown-list/item.ts | 6 ++++++ web/app/components/x/dropdown-list/link-to.hbs | 2 +- web/app/components/x/dropdown-list/link-to.ts | 9 ++++----- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/web/app/components/x/dropdown-list/item.ts b/web/app/components/x/dropdown-list/item.ts index dec0c12ac..136f104ef 100644 --- a/web/app/components/x/dropdown-list/item.ts +++ b/web/app/components/x/dropdown-list/item.ts @@ -124,6 +124,12 @@ export default class XDropdownListItemComponent extends Component handling. + */ @action onClick() { if (this.args.onItemClick) { this.args.onItemClick(this.args.value, this.args.attributes); diff --git a/web/app/components/x/dropdown-list/link-to.hbs b/web/app/components/x/dropdown-list/link-to.hbs index eed9d6036..5fbbe9228 100644 --- a/web/app/components/x/dropdown-list/link-to.hbs +++ b/web/app/components/x/dropdown-list/link-to.hbs @@ -2,7 +2,7 @@ data-test-x-dropdown-list-item-link-to {{did-insert @registerElement}} {{on "mouseenter" @focusMouseTarget}} - {{on "click" this.hideDropdown}} + {{on "click" this.onClick}} role={{@role}} aria-selected={{@isAriaSelected}} tabindex="-1" diff --git a/web/app/components/x/dropdown-list/link-to.ts b/web/app/components/x/dropdown-list/link-to.ts index 1386de9a2..ba562273c 100644 --- a/web/app/components/x/dropdown-list/link-to.ts +++ b/web/app/components/x/dropdown-list/link-to.ts @@ -11,7 +11,6 @@ interface XDropdownListLinkToComponentSignature { query?: Record; model?: unknown; models?: unknown[]; - hideContent: () => void; }; Blocks: { default: []; @@ -20,17 +19,17 @@ interface XDropdownListLinkToComponentSignature { export default class XDropdownListLinkToComponent extends Component { /** - * The action to close the dropdown. Called on click. + * The action to run when the item is clicked. * We wait until the next run loop so that we don't interfere with * Ember's handling. Because this approach causes issues * when testing, we use `schedule` as an approximation. */ - @action protected hideDropdown(): void { + @action protected onClick(): void { if (Ember.testing) { - schedule("afterRender", this.args.hideContent); + schedule("afterRender", this.args.onClick); } else { next(() => { - this.args.hideContent(); + this.args.onClick(); }); } } From 4e95fc93bbb5bcdaeeadb9d9ea4f68f37372b1db Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Thu, 4 Jan 2024 14:58:03 -0500 Subject: [PATCH 31/32] Post-merge fix --- web/app/components/x/dropdown-list/item.hbs | 1 - 1 file changed, 1 deletion(-) diff --git a/web/app/components/x/dropdown-list/item.hbs b/web/app/components/x/dropdown-list/item.hbs index 0a1a34de7..90bf58135 100644 --- a/web/app/components/x/dropdown-list/item.hbs +++ b/web/app/components/x/dropdown-list/item.hbs @@ -55,7 +55,6 @@ registerElement=this.registerElement focusMouseTarget=(perform this.maybeFocusMouseTarget) onClick=this.onClick - hideContent=@hideContent ) ExternalLink=(component "x/dropdown-list/external-link" From 55cc953105cfb4322387f88a6e7a2ffcd4602aeb Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Fri, 12 Jan 2024 16:52:03 -0500 Subject: [PATCH 32/32] Fix merge errors --- web/app/components/project/index.hbs | 6 +++--- web/app/components/project/index.ts | 8 ++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/web/app/components/project/index.hbs b/web/app/components/project/index.hbs index 9668452af..c4243c3a3 100644 --- a/web/app/components/project/index.hbs +++ b/web/app/components/project/index.hbs @@ -25,7 +25,7 @@ <:anchor as |dd|> @@ -55,7 +55,7 @@
    @@ -140,7 +140,7 @@ diff --git a/web/app/components/project/index.ts b/web/app/components/project/index.ts index 5fae33c23..8a9d90d3f 100644 --- a/web/app/components/project/index.ts +++ b/web/app/components/project/index.ts @@ -441,7 +441,9 @@ export default class ProjectIndexComponent extends Component