├── .github
└── workflows
│ ├── ci.yml
│ └── docker.yml
├── .gitignore
├── .idea
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
└── vcs.xml
├── .scala-steward.conf
├── LICENSE
├── README.md
├── auth
├── .js
│ └── src
│ │ ├── main
│ │ └── scala
│ │ │ └── io
│ │ │ └── udash
│ │ │ └── auth
│ │ │ ├── AuthApplication.scala
│ │ │ ├── AuthPresenter.scala
│ │ │ └── AuthView.scala
│ │ └── test
│ │ └── scala
│ │ └── io
│ │ └── udash
│ │ └── auth
│ │ ├── AuthApplicationTest.scala
│ │ ├── AuthFrontendTestUtils.scala
│ │ ├── AuthPresenterTest.scala
│ │ └── AuthViewTest.scala
└── src
│ ├── main
│ └── scala
│ │ └── io
│ │ └── udash
│ │ └── auth
│ │ ├── AuthRequires.scala
│ │ ├── DefaultAuthExceptionCodecRegistry.scala
│ │ ├── Permission.scala
│ │ ├── PermissionCombinator.scala
│ │ ├── PermissionId.scala
│ │ ├── UserCtx.scala
│ │ └── exceptions.scala
│ └── test
│ └── scala
│ └── io
│ └── udash
│ └── auth
│ ├── AuthRequiresTest.scala
│ ├── AuthTestUtils.scala
│ ├── PermissionCombinatorTest.scala
│ └── PermissionTest.scala
├── benchmarks
└── .js
│ ├── index.html
│ └── src
│ └── main
│ └── scala
│ └── io
│ └── udash
│ └── benchmarks
│ ├── Main.scala
│ ├── css
│ └── CssStylesApply.scala
│ ├── i18n
│ └── StaticTranslationBinding.scala
│ └── properties
│ ├── BenchmarkUtils.scala
│ ├── FilteredSeqPropertyListeners.scala
│ ├── ModelPropertyListeners.scala
│ ├── ModelPropertyWithSeqListeners.scala
│ ├── PropertyParameters.scala
│ ├── ReversedSeqPropertyListeners.scala
│ ├── SinglePropertyListeners.scala
│ ├── TransformedSeqPropertyListeners.scala
│ └── ZippedSeqPropertyListeners.scala
├── bootstrap4
└── .js
│ └── src
│ ├── main
│ └── scala
│ │ └── io
│ │ └── udash
│ │ └── bootstrap
│ │ ├── UdashBootstrap.scala
│ │ ├── alert
│ │ ├── DismissibleUdashAlert.scala
│ │ ├── UdashAlert.scala
│ │ └── UdashAlertBase.scala
│ │ ├── badge
│ │ └── UdashBadge.scala
│ │ ├── breadcrumb
│ │ └── UdashBreadcrumbs.scala
│ │ ├── button
│ │ ├── UdashButton.scala
│ │ ├── UdashButtonGroup.scala
│ │ └── UdashButtonToolbar.scala
│ │ ├── card
│ │ └── UdashCard.scala
│ │ ├── carousel
│ │ └── UdashCarousel.scala
│ │ ├── collapse
│ │ ├── UdashAccordion.scala
│ │ └── UdashCollapse.scala
│ │ ├── datepicker
│ │ └── UdashDatePicker.scala
│ │ ├── dropdown
│ │ └── UdashDropdown.scala
│ │ ├── form
│ │ ├── UdashForm.scala
│ │ ├── UdashInputGroup.scala
│ │ └── Validator.scala
│ │ ├── jumbotron
│ │ └── UdashJumbotron.scala
│ │ ├── list
│ │ └── UdashListGroup.scala
│ │ ├── modal
│ │ └── UdashModal.scala
│ │ ├── nav
│ │ ├── UdashNav.scala
│ │ └── UdashNavbar.scala
│ │ ├── package.scala
│ │ ├── pagination
│ │ └── UdashPagination.scala
│ │ ├── progressbar
│ │ └── UdashProgressBar.scala
│ │ ├── table
│ │ └── UdashTable.scala
│ │ ├── tooltip
│ │ ├── TooltipEvent.scala
│ │ ├── TooltipUtils.scala
│ │ ├── UdashPopover.scala
│ │ └── UdashTooltip.scala
│ │ └── utils
│ │ ├── BootstrapImplicits.scala
│ │ ├── BootstrapStyles.scala
│ │ ├── BootstrapTags.scala
│ │ ├── UdashBootstrapComponent.scala
│ │ └── UdashIcons.scala
│ └── test
│ ├── scala-2.12
│ └── io
│ │ └── udash
│ │ └── bootstrap
│ │ └── form
│ │ └── UdashFormTest.scala
│ └── scala
│ └── io
│ └── udash
│ └── bootstrap
│ ├── BootstrapImplicitsTest.scala
│ ├── alert
│ └── UdashAlertTest.scala
│ ├── badge
│ └── UdashBadgeTest.scala
│ ├── breadcrumb
│ └── UdashBreadcrumbsTest.scala
│ ├── button
│ ├── UdashButtonGroupTest.scala
│ └── UdashButtonTest.scala
│ ├── card
│ └── UdashCardTest.scala
│ ├── carousel
│ └── UdashCarouselTest.scala
│ ├── collapse
│ ├── UdashAccordionTest.scala
│ └── UdashCollapseTest.scala
│ ├── datepicker
│ └── UdashDatePickerTest.scala
│ ├── dropdown
│ └── UdashDropdownTest.scala
│ ├── form
│ └── UdashInputGroupTest.scala
│ ├── jumbotron
│ └── UdashJumbotronTest.scala
│ ├── list
│ └── UdashListGroupTest.scala
│ ├── modal
│ └── UdashModalTest.scala
│ ├── nav
│ ├── UdashNavTest.scala
│ └── UdashNavbarTest.scala
│ ├── pagination
│ └── UdashPaginationTest.scala
│ ├── progressbar
│ └── UdashProgressBarTest.scala
│ ├── table
│ └── UdashTableTest.scala
│ ├── tooltip
│ ├── PopoverTest.scala
│ ├── TooltipTest.scala
│ └── TooltipTestUtils.scala
│ └── utils
│ └── UdashIconsTest.scala
├── build.sbt
├── core
├── .js
│ └── src
│ │ ├── main
│ │ └── scala
│ │ │ └── io
│ │ │ └── udash
│ │ │ ├── Application.scala
│ │ │ ├── bindings
│ │ │ ├── Bindings.scala
│ │ │ ├── inputs
│ │ │ │ ├── CheckButtons.scala
│ │ │ │ ├── Checkbox.scala
│ │ │ │ ├── FileInput.scala
│ │ │ │ ├── GroupedButtonsBinding.scala
│ │ │ │ ├── Input.scala
│ │ │ │ ├── InputBinding.scala
│ │ │ │ ├── RadioButtons.scala
│ │ │ │ ├── RangeInput.scala
│ │ │ │ ├── Select.scala
│ │ │ │ ├── SelectBinding.scala
│ │ │ │ ├── TextArea.scala
│ │ │ │ └── TextInputsModifier.scala
│ │ │ └── modifiers
│ │ │ │ ├── Binding.scala
│ │ │ │ ├── DOMManipulator.scala
│ │ │ │ ├── EmptyModifier.scala
│ │ │ │ ├── PropertyModifier.scala
│ │ │ │ ├── SeqAsValueModifier.scala
│ │ │ │ ├── SeqPropertyModifier.scala
│ │ │ │ ├── SeqPropertyModifierUtils.scala
│ │ │ │ ├── SeqPropertyWithIndexModifier.scala
│ │ │ │ ├── SimplePropertyModifier.scala
│ │ │ │ ├── ValueModifier.scala
│ │ │ │ └── package.scala
│ │ │ ├── component
│ │ │ ├── Component.scala
│ │ │ ├── ComponentId.scala
│ │ │ ├── Components.scala
│ │ │ └── Listenable.scala
│ │ │ ├── core
│ │ │ ├── Defaults.scala
│ │ │ └── Definitions.scala
│ │ │ ├── package.scala
│ │ │ ├── routing
│ │ │ ├── Routing.scala
│ │ │ ├── RoutingEngine.scala
│ │ │ ├── RoutingRegistry.scala
│ │ │ ├── UrlChangeProvider.scala
│ │ │ └── UrlLogging.scala
│ │ │ ├── utils
│ │ │ ├── FileService.scala
│ │ │ └── FileUploader.scala
│ │ │ └── view
│ │ │ └── ViewRenderer.scala
│ │ └── test
│ │ └── scala
│ │ ├── io
│ │ └── udash
│ │ │ ├── ApplicationTest.scala
│ │ │ ├── bindings
│ │ │ ├── QueuedNodeModifierTest.scala
│ │ │ ├── TagsBindingTest.scala
│ │ │ └── inputs
│ │ │ │ ├── CheckButtonsTest.scala
│ │ │ │ ├── CheckboxTest.scala
│ │ │ │ ├── InputTest.scala
│ │ │ │ ├── RadioButtonsTest.scala
│ │ │ │ ├── RangeInputTest.scala
│ │ │ │ ├── SelectTest.scala
│ │ │ │ └── TextAreaTest.scala
│ │ │ ├── component
│ │ │ ├── ComponentIdTest.scala
│ │ │ └── ListenableTest.scala
│ │ │ ├── routing
│ │ │ ├── RoutingEngineTest.scala
│ │ │ ├── RoutingOperatorTest.scala
│ │ │ ├── StateTest.scala
│ │ │ ├── UrlLoggingTest.scala
│ │ │ ├── WindowUrlFragmentChangeProviderTest.scala
│ │ │ └── WindowUrlPathChangeProviderTest.scala
│ │ │ ├── testing
│ │ │ ├── TestRouting.scala
│ │ │ ├── TestRoutingRegistry.scala
│ │ │ ├── TestState.scala
│ │ │ ├── TestUrlChangeProvider.scala
│ │ │ ├── TestViewFactory.scala
│ │ │ ├── TestViewFactoryRegistry.scala
│ │ │ ├── TestViewRenderer.scala
│ │ │ └── UdashCoreFrontendTest.scala
│ │ │ └── view
│ │ │ └── ViewRendererTest.scala
│ │ └── manual
│ │ └── PropertyErrorManualTest.scala
└── src
│ ├── main
│ └── scala
│ │ └── io
│ │ └── udash
│ │ └── properties
│ │ ├── Blank.scala
│ │ ├── CallbackSequencer.scala
│ │ ├── HasGenCodecAndModelPropertyCreator.scala
│ │ ├── HasModelPropertyCreator.scala
│ │ ├── ImmutableProperty.scala
│ │ ├── IsModelPropertyTemplate.scala
│ │ ├── MutableSetRegistration.scala
│ │ ├── Properties.scala
│ │ ├── PropertyCreator.scala
│ │ ├── PropertyCreatorImplicits.scala
│ │ ├── model
│ │ ├── ModelProperty.scala
│ │ ├── ModelPropertyImpl.scala
│ │ └── ReadableModelProperty.scala
│ │ ├── seq
│ │ ├── CombinedReadableSeqProperty.scala
│ │ ├── DirectSeqProperty.scala
│ │ ├── FilteredSeqProperty.scala
│ │ ├── ForwarderReadableSeqProperty.scala
│ │ ├── Patch.scala
│ │ ├── PropertySeqCombinedReadableSeqProperty.scala
│ │ ├── ReadableSeqProperty.scala
│ │ ├── ReversedSeqProperty.scala
│ │ ├── SeqProperty.scala
│ │ ├── SeqPropertyFromSingleValue.scala
│ │ ├── TransformedSeqProperty.scala
│ │ └── ZippedSeqProperty.scala
│ │ └── single
│ │ ├── CastableProperty.scala
│ │ ├── CombinedProperty.scala
│ │ ├── DirectProperty.scala
│ │ ├── ForwarderProperty.scala
│ │ ├── Property.scala
│ │ ├── ReadableProperty.scala
│ │ └── TransformedProperty.scala
│ └── test
│ └── scala
│ └── io
│ └── udash
│ ├── properties
│ ├── BlankTest.scala
│ ├── CallbackSequencerTest.scala
│ ├── HasGenCodecAndModelPropertyCreatorTest.scala
│ ├── ImmutablePropertyTest.scala
│ ├── ModelPropertyTest.scala
│ ├── PropertyCreatorTest.scala
│ ├── PropertyTest.scala
│ ├── PropertyUsageTest.scala
│ ├── SeqPropertyTest.scala
│ └── UtilsTest.scala
│ └── testing
│ ├── AsyncUdashCoreTest.scala
│ ├── CoreTestUtils.scala
│ └── UdashCoreTest.scala
├── css
├── .js
│ └── src
│ │ ├── main
│ │ └── scala
│ │ │ └── io
│ │ │ └── udash
│ │ │ └── css
│ │ │ └── CssView.scala
│ │ └── test
│ │ └── scala
│ │ └── io
│ │ └── udash
│ │ └── css
│ │ └── CssViewTest.scala
├── .jvm
│ └── src
│ │ ├── main
│ │ └── scala
│ │ │ └── io
│ │ │ └── udash
│ │ │ └── css
│ │ │ ├── CssFileRenderer.scala
│ │ │ └── CssStringRenderer.scala
│ │ └── test
│ │ └── scala
│ │ └── io
│ │ └── udash
│ │ └── css
│ │ ├── CssFileRendererTest.scala
│ │ └── CssStringRendererTest.scala
└── src
│ ├── main
│ └── scala
│ │ └── io
│ │ └── udash
│ │ └── css
│ │ ├── CssBase.scala
│ │ ├── CssStyle.scala
│ │ └── CssText.scala
│ └── test
│ └── scala
│ └── io
│ └── udash
│ └── css
│ ├── CssTextTest.scala
│ ├── SecondStylesheetExample.scala
│ └── StylesheetExample.scala
├── guide
├── backend
│ └── src
│ │ └── main
│ │ ├── resources
│ │ ├── demo_translations_en.properties
│ │ ├── demo_translations_pl.properties
│ │ ├── logback.xml
│ │ └── reference.conf
│ │ └── scala
│ │ └── io
│ │ └── udash
│ │ └── web
│ │ ├── Implicits.scala
│ │ ├── Launcher.scala
│ │ ├── guide
│ │ ├── demos
│ │ │ ├── DemosServer.scala
│ │ │ ├── activity
│ │ │ │ ├── CallLogger.scala
│ │ │ │ └── CallServer.scala
│ │ │ ├── i18n
│ │ │ │ └── TranslationServer.scala
│ │ │ └── rpc
│ │ │ │ ├── ClientIdServer.scala
│ │ │ │ ├── ExceptionsServer.scala
│ │ │ │ ├── GenCodecServer.scala
│ │ │ │ ├── NotificationsServer.scala
│ │ │ │ └── PingServer.scala
│ │ ├── markdown
│ │ │ └── MarkdownPagesEndpoint.scala
│ │ ├── rest
│ │ │ └── ExposedRestInterfaces.scala
│ │ └── rpc
│ │ │ ├── ClientRPC.scala
│ │ │ └── ExposedRpcInterfaces.scala
│ │ ├── server
│ │ └── ApplicationServer.scala
│ │ └── styles
│ │ └── CssRenderer.scala
├── commons
│ └── .js
│ │ └── src
│ │ └── main
│ │ ├── assets
│ │ ├── pdf
│ │ │ └── origami_crane_printok.pdf
│ │ ├── styles
│ │ │ └── prism.css
│ │ └── svg
│ │ │ ├── avsystem.svg
│ │ │ ├── based.svg
│ │ │ ├── compiled.svg
│ │ │ ├── github.svg
│ │ │ ├── gitter.svg
│ │ │ ├── icon_submenu.svg
│ │ │ ├── shared_code.svg
│ │ │ ├── stack.svg
│ │ │ ├── todomvc.svg
│ │ │ └── typesafe.svg
│ │ ├── resources
│ │ └── prism.js
│ │ └── scala
│ │ └── io
│ │ └── udash
│ │ └── web
│ │ └── commons
│ │ ├── components
│ │ ├── CodeBlock.scala
│ │ ├── Footer.scala
│ │ ├── ForceBootstrap.scala
│ │ ├── HeaderButtons.scala
│ │ └── HeaderNav.scala
│ │ ├── config
│ │ └── ExternalUrls.scala
│ │ └── views
│ │ ├── Component.scala
│ │ ├── ImageFactory.scala
│ │ └── MarkdownView.scala
├── guide
│ └── .js
│ │ └── src
│ │ ├── main
│ │ ├── assets
│ │ │ ├── assets.less
│ │ │ ├── fonts
│ │ │ │ └── roboto
│ │ │ │ │ ├── Roboto-Black.ttf
│ │ │ │ │ ├── Roboto-BlackItalic.ttf
│ │ │ │ │ ├── Roboto-Bold.ttf
│ │ │ │ │ ├── Roboto-BoldItalic.ttf
│ │ │ │ │ ├── Roboto-Italic.ttf
│ │ │ │ │ ├── Roboto-Light.ttf
│ │ │ │ │ ├── Roboto-LightItalic.ttf
│ │ │ │ │ ├── Roboto-Medium.ttf
│ │ │ │ │ ├── Roboto-MediumItalic.ttf
│ │ │ │ │ ├── Roboto-Regular.ttf
│ │ │ │ │ ├── Roboto-Thin.ttf
│ │ │ │ │ └── Roboto-ThinItalic.ttf
│ │ │ ├── images
│ │ │ │ ├── ext
│ │ │ │ │ └── bootstrap
│ │ │ │ │ │ └── carousel.jpg
│ │ │ │ ├── favicon.ico
│ │ │ │ ├── intro_bg.jpg
│ │ │ │ ├── intro_bird.png
│ │ │ │ ├── quick
│ │ │ │ │ └── generator.png
│ │ │ │ ├── share
│ │ │ │ │ ├── share_facebook.png
│ │ │ │ │ ├── share_google.png
│ │ │ │ │ └── share_twitter.png
│ │ │ │ ├── udash_logo.png
│ │ │ │ ├── udash_logo_l.png
│ │ │ │ ├── udash_logo_m.png
│ │ │ │ └── views
│ │ │ │ │ ├── bootstrapping
│ │ │ │ │ ├── modules_basic.png
│ │ │ │ │ ├── modules_extended.png
│ │ │ │ │ └── states.png
│ │ │ │ │ ├── frontend
│ │ │ │ │ ├── mvp.png
│ │ │ │ │ ├── property.png
│ │ │ │ │ └── propertyhierarchy.png
│ │ │ │ │ └── rest
│ │ │ │ │ ├── openapi.svg
│ │ │ │ │ └── serialization.svg
│ │ │ ├── index.html
│ │ │ └── pages
│ │ │ │ ├── intro.md
│ │ │ │ ├── license.md
│ │ │ │ └── rest.md
│ │ └── scala
│ │ │ └── io
│ │ │ └── udash
│ │ │ └── web
│ │ │ └── guide
│ │ │ ├── RoutingRegistryDef.scala
│ │ │ ├── RoutingStatesDef.scala
│ │ │ ├── StatesToViewFactoryDef.scala
│ │ │ ├── components
│ │ │ ├── BootstrapUtils.scala
│ │ │ ├── GuideMenu.scala
│ │ │ └── Header.scala
│ │ │ ├── demos
│ │ │ ├── AutoDemo.scala
│ │ │ ├── DemosClient.scala
│ │ │ └── rpc
│ │ │ │ ├── NotificationsClient.scala
│ │ │ │ └── PingClient.scala
│ │ │ ├── init.scala
│ │ │ ├── rpc
│ │ │ └── RPCService.scala
│ │ │ └── views
│ │ │ ├── ContentView.scala
│ │ │ ├── ErrorView.scala
│ │ │ ├── FaqView.scala
│ │ │ ├── References.scala
│ │ │ ├── RootView.scala
│ │ │ ├── Versions.scala
│ │ │ ├── ViewContainer.scala
│ │ │ ├── bootstrapping
│ │ │ ├── AdvancedBootstrappingSbtView.scala
│ │ │ ├── BootstrappingBackendView.scala
│ │ │ ├── BootstrappingFrontendView.scala
│ │ │ ├── BootstrappingIntroView.scala
│ │ │ ├── BootstrappingRpcView.scala
│ │ │ ├── BootstrappingSbtView.scala
│ │ │ └── BootstrappingView.scala
│ │ │ ├── ext
│ │ │ ├── AuthorizationExtView.scala
│ │ │ ├── BootstrapExtView.scala
│ │ │ ├── I18NExtView.scala
│ │ │ ├── JQueryExtView.scala
│ │ │ ├── UserActivityExtView.scala
│ │ │ └── demo
│ │ │ │ ├── DynamicRemoteTranslationsDemo.scala
│ │ │ │ ├── FrontendTranslationsDemo.scala
│ │ │ │ ├── JQueryCallbacksDemo.scala
│ │ │ │ ├── JQueryEventsDemo.scala
│ │ │ │ ├── RemoteTranslationsDemo.scala
│ │ │ │ ├── RpcLoggingDemo.scala
│ │ │ │ ├── UrlLoggingDemo.scala
│ │ │ │ └── bootstrap
│ │ │ │ ├── AccordionDemo.scala
│ │ │ │ ├── AlertsDemo.scala
│ │ │ │ ├── BadgesDemo.scala
│ │ │ │ ├── BreadcrumbsDemo.scala
│ │ │ │ ├── ButtonDropdownDemo.scala
│ │ │ │ ├── ButtonToolbarDemo.scala
│ │ │ │ ├── ButtonsDemo.scala
│ │ │ │ ├── CardsDemo.scala
│ │ │ │ ├── CarouselDemo.scala
│ │ │ │ ├── CheckboxButtonsDemo.scala
│ │ │ │ ├── DatePickerDemo.scala
│ │ │ │ ├── DateRangePickerDemo.scala
│ │ │ │ ├── DropdownsDemo.scala
│ │ │ │ ├── IconsDemo.scala
│ │ │ │ ├── InlineFormDemo.scala
│ │ │ │ ├── InputGroupDemo.scala
│ │ │ │ ├── JumbotronDemo.scala
│ │ │ │ ├── LabelsDemo.scala
│ │ │ │ ├── ListGroupDemo.scala
│ │ │ │ ├── NavbarDemo.scala
│ │ │ │ ├── NavigationDemo.scala
│ │ │ │ ├── NavsDemo.scala
│ │ │ │ ├── PaginationDemo.scala
│ │ │ │ ├── PopoversDemo.scala
│ │ │ │ ├── ProgressBarDemo.scala
│ │ │ │ ├── RadioButtonsDemo.scala
│ │ │ │ ├── ResponsiveEmbedDemo.scala
│ │ │ │ ├── SimpleCollapseDemo.scala
│ │ │ │ ├── SimpleFormDemo.scala
│ │ │ │ ├── SimpleModalDemo.scala
│ │ │ │ ├── StaticButtonsGroupDemo.scala
│ │ │ │ ├── StaticsDemo.scala
│ │ │ │ ├── TableDemo.scala
│ │ │ │ ├── ToggleButtonsDemo.scala
│ │ │ │ └── TooltipsDemo.scala
│ │ │ ├── frontend
│ │ │ ├── FrontendBindingsView.scala
│ │ │ ├── FrontendFilesView.scala
│ │ │ ├── FrontendFormsView.scala
│ │ │ ├── FrontendIntroView.scala
│ │ │ ├── FrontendMVPView.scala
│ │ │ ├── FrontendPropertiesView.scala
│ │ │ ├── FrontendRoutingView.scala
│ │ │ ├── FrontendTemplatesView.scala
│ │ │ ├── FrontendView.scala
│ │ │ └── demos
│ │ │ │ ├── BindAttributeDemo.scala
│ │ │ │ ├── BindDemo.scala
│ │ │ │ ├── CheckButtonsDemo.scala
│ │ │ │ ├── CheckboxDemo.scala
│ │ │ │ ├── DateDemo.scala
│ │ │ │ ├── DateTimeLocalDemo.scala
│ │ │ │ ├── FileInputDemo.scala
│ │ │ │ ├── IntroFormDemo.scala
│ │ │ │ ├── MultiSelectDemo.scala
│ │ │ │ ├── ProduceDemo.scala
│ │ │ │ ├── RadioButtonsDemo.scala
│ │ │ │ ├── RepeatDemo.scala
│ │ │ │ ├── SelectDemo.scala
│ │ │ │ ├── ShowIfDemo.scala
│ │ │ │ ├── TextAreaDemo.scala
│ │ │ │ ├── TextInputDemo.scala
│ │ │ │ └── TimeDemo.scala
│ │ │ └── rpc
│ │ │ ├── RpcClientServerView.scala
│ │ │ ├── RpcInterfacesView.scala
│ │ │ ├── RpcIntroView.scala
│ │ │ ├── RpcSerializationView.scala
│ │ │ ├── RpcServerClientView.scala
│ │ │ ├── RpcView.scala
│ │ │ └── demos
│ │ │ ├── ClientIdDemoComponent.scala
│ │ │ ├── ExceptionsDemoComponent.scala
│ │ │ ├── GenCodecsDemoComponent.scala
│ │ │ ├── NotificationsDemoComponent.scala
│ │ │ ├── PingPongCallDemoComponent.scala
│ │ │ └── PingPongPushDemoComponent.scala
│ │ └── test
│ │ └── scala
│ │ └── io
│ │ └── udash
│ │ └── web
│ │ └── guide
│ │ └── demos
│ │ └── AutoDemoTest.scala
├── homepage
│ └── .js
│ │ └── src
│ │ └── main
│ │ ├── assets
│ │ ├── assets.less
│ │ ├── fonts
│ │ │ └── roboto
│ │ │ │ ├── Roboto-Black.ttf
│ │ │ │ ├── Roboto-BlackItalic.ttf
│ │ │ │ ├── Roboto-Bold.ttf
│ │ │ │ ├── Roboto-BoldItalic.ttf
│ │ │ │ ├── Roboto-Italic.ttf
│ │ │ │ ├── Roboto-Light.ttf
│ │ │ │ ├── Roboto-LightItalic.ttf
│ │ │ │ ├── Roboto-Medium.ttf
│ │ │ │ ├── Roboto-MediumItalic.ttf
│ │ │ │ ├── Roboto-Regular.ttf
│ │ │ │ ├── Roboto-Thin.ttf
│ │ │ │ └── Roboto-ThinItalic.ttf
│ │ ├── images
│ │ │ ├── favicon.ico
│ │ │ ├── features_compiled.png
│ │ │ ├── features_shared.png
│ │ │ ├── features_typesafe.png
│ │ │ ├── intro_bg.jpg
│ │ │ ├── intro_bird.png
│ │ │ ├── laptop.png
│ │ │ ├── share
│ │ │ │ ├── share_facebook.png
│ │ │ │ ├── share_google.png
│ │ │ │ └── share_twitter.png
│ │ │ ├── udash_logo.png
│ │ │ ├── udash_logo_l.png
│ │ │ └── udash_logo_m.png
│ │ └── index.html
│ │ └── scala
│ │ └── io
│ │ └── udash
│ │ └── web
│ │ └── homepage
│ │ ├── RoutingRegistryDef.scala
│ │ ├── StatesToViewFactoryDef.scala
│ │ ├── components
│ │ ├── Buttons.scala
│ │ ├── Header.scala
│ │ └── demo
│ │ │ ├── CodeDemo.scala
│ │ │ └── DemoComponent.scala
│ │ ├── init.scala
│ │ ├── states.scala
│ │ └── views
│ │ ├── ErrorView.scala
│ │ ├── IndexView.scala
│ │ └── RootView.scala
├── packager
│ └── src
│ │ └── main
│ │ └── resources
│ │ └── application.conf
├── selenium
│ └── src
│ │ └── test
│ │ ├── resources
│ │ └── application.conf
│ │ └── scala
│ │ └── io
│ │ └── udash
│ │ └── web
│ │ ├── SeleniumTest.scala
│ │ └── guide
│ │ └── demos
│ │ ├── ext
│ │ ├── I18nDemosTest.scala
│ │ └── JQueryDemosTest.scala
│ │ ├── frontend
│ │ ├── FrontendBindingsTest.scala
│ │ ├── FrontendFormsTest.scala
│ │ ├── FrontendIntroTest.scala
│ │ └── FrontendRoutingTest.scala
│ │ └── rpc
│ │ ├── RpcBackendTest.scala
│ │ ├── RpcFrontendTest.scala
│ │ ├── RpcIntroTest.scala
│ │ └── RpcSerializationTest.scala
└── shared
│ └── src
│ └── main
│ └── scala
│ └── io
│ └── udash
│ └── web
│ ├── commons
│ └── styles
│ │ ├── DefaultStyles.scala
│ │ ├── GlobalStyles.scala
│ │ ├── attributes
│ │ └── Attributes.scala
│ │ ├── components
│ │ ├── CodeBlockStyles.scala
│ │ ├── FooterStyles.scala
│ │ ├── HeaderButtonsStyles.scala
│ │ ├── HeaderNavStyles.scala
│ │ └── MobileMenuStyles.scala
│ │ └── utils
│ │ ├── CommonStyleUtils.scala
│ │ ├── MediaQueries.scala
│ │ ├── StyleConstants.scala
│ │ └── UdashFonts.scala
│ ├── guide
│ ├── GuideExceptions.scala
│ ├── MainClientRPC.scala
│ ├── MainServerRPC.scala
│ ├── demos
│ │ ├── DemosClientRPC.scala
│ │ ├── DemosServerRPC.scala
│ │ ├── activity
│ │ │ ├── Call.scala
│ │ │ └── CallServerRPC.scala
│ │ ├── i18n
│ │ │ └── Translations.scala
│ │ ├── rest
│ │ │ ├── MainServerREST.scala
│ │ │ └── RestExampleClass.scala
│ │ └── rpc
│ │ │ ├── ClientIdServerRPC.scala
│ │ │ ├── ExceptionsRPC.scala
│ │ │ ├── GenCodecServerRPC.scala
│ │ │ ├── NotificationsClientRPC.scala
│ │ │ ├── NotificationsServerRPC.scala
│ │ │ ├── PingClientRPC.scala
│ │ │ └── PingServerRPC.scala
│ ├── markdown
│ │ ├── MarkdownPage.scala
│ │ └── MarkdownPageRPC.scala
│ └── styles
│ │ ├── GuideDefaultStyles.scala
│ │ ├── MarkdownStyles.scala
│ │ ├── demo
│ │ ├── ExampleKeyframes.scala
│ │ ├── ExampleMixins.scala
│ │ └── ExampleStyles.scala
│ │ ├── partials
│ │ ├── GuideStyles.scala
│ │ ├── HeaderStyles.scala
│ │ └── MenuStyles.scala
│ │ └── utils
│ │ ├── GuideStyleUtils.scala
│ │ └── MediaQueries.scala
│ └── homepage
│ └── styles
│ ├── HomepageDefaultStyles.scala
│ └── partials
│ ├── ButtonsStyle.scala
│ ├── DemoStyles.scala
│ ├── HeaderStyles.scala
│ └── HomepageStyles.scala
├── i18n
├── .js
│ └── src
│ │ ├── main
│ │ └── scala
│ │ │ └── io
│ │ │ └── udash
│ │ │ └── i18n
│ │ │ ├── FrontendTranslationProvider.scala
│ │ │ ├── LocalTranslationProvider.scala
│ │ │ ├── RemoteTranslationProvider.scala
│ │ │ ├── Translations.scala
│ │ │ ├── bindings
│ │ │ ├── AttrTranslationModifier.scala
│ │ │ ├── DynamicAttrTranslationBinding.scala
│ │ │ ├── DynamicTranslationBinding.scala
│ │ │ ├── TranslationModifier.scala
│ │ │ └── package.scala
│ │ │ └── package.scala
│ │ └── test
│ │ └── scala
│ │ └── io
│ │ └── udash
│ │ └── i18n
│ │ ├── BindingsTest.scala
│ │ ├── LocalTranslationProviderTest.scala
│ │ └── RemoteTranslationProviderTest.scala
├── .jvm
│ └── src
│ │ ├── main
│ │ └── scala
│ │ │ └── io
│ │ │ └── udash
│ │ │ └── i18n
│ │ │ ├── ResourceBundlesTranslationTemplatesProvider.scala
│ │ │ ├── TranslationRPCEndpoint.scala
│ │ │ └── TranslationTemplatesProvider.scala
│ │ └── test
│ │ ├── resources
│ │ ├── mixed_en.properties
│ │ ├── mixed_pl.properties
│ │ ├── test2_translations_en.properties
│ │ ├── test2_translations_pl.properties
│ │ ├── test_translations_en.properties
│ │ └── test_translations_pl.properties
│ │ └── scala
│ │ └── io
│ │ └── udash
│ │ └── i18n
│ │ ├── ResourceBundlesTranslationTemplatesProviderTest.scala
│ │ └── TranslationRPCEndpointTest.scala
└── src
│ ├── main
│ └── scala
│ │ └── io
│ │ └── udash
│ │ └── i18n
│ │ ├── RemoteTranslationRPC.scala
│ │ ├── TranslationKey.scala
│ │ ├── TranslationProvider.scala
│ │ └── types.scala
│ └── test
│ └── scala
│ └── io
│ └── udash
│ └── i18n
│ ├── TranslationKeyTest.scala
│ └── Utils.scala
├── macros
└── src
│ └── main
│ └── scala
│ └── io
│ └── udash
│ ├── css
│ └── macros
│ │ └── StyleMacros.scala
│ └── macros
│ ├── AllValuesMacro.scala
│ ├── ComponentIdMacro.scala
│ ├── PropertyMacros.scala
│ ├── RestMacros.scala
│ └── TestMacros.scala
├── package-lock.json
├── package.json
├── project
├── Dependencies.scala
├── build.properties
└── plugins.sbt
├── rest
├── .js
│ └── src
│ │ └── main
│ │ └── scala
│ │ └── io
│ │ └── udash
│ │ └── rest
│ │ └── DefaultSttpBackend.scala
├── .jvm
│ └── src
│ │ ├── main
│ │ └── scala
│ │ │ └── io
│ │ │ └── udash
│ │ │ └── rest
│ │ │ ├── DefaultSttpBackend.scala
│ │ │ ├── RestServlet.scala
│ │ │ └── openapi
│ │ │ └── OpenApiServlet.scala
│ │ └── test
│ │ ├── resources
│ │ └── RestTestApi.json
│ │ └── scala
│ │ └── io
│ │ └── udash
│ │ └── rest
│ │ ├── EndpointsIntegrationTest.scala
│ │ ├── ServerTestRESTInterface.scala
│ │ ├── ServletBasedRestApiTest.scala
│ │ ├── SomeApi.scala
│ │ ├── SttpRestCallTest.scala
│ │ ├── UsesHttpServer.scala
│ │ ├── examples
│ │ ├── ClientMain.scala
│ │ ├── GenericApi.scala
│ │ ├── ServerMain.scala
│ │ └── UserApi.scala
│ │ └── openapi
│ │ └── OpenApiGenerationTest.scala
├── jetty
│ └── src
│ │ ├── main
│ │ └── scala
│ │ │ └── io
│ │ │ └── udash
│ │ │ └── rest
│ │ │ └── jetty
│ │ │ └── JettyRestClient.scala
│ │ └── test
│ │ └── scala
│ │ └── io
│ │ └── udash
│ │ └── rest
│ │ └── jetty
│ │ └── JettyRestCallTest.scala
└── src
│ ├── main
│ └── scala
│ │ └── io
│ │ └── udash
│ │ └── rest
│ │ ├── DefaultRestApiCompanion.scala
│ │ ├── RestDataCompanion.scala
│ │ ├── RestException.scala
│ │ ├── SttpRestClient.scala
│ │ ├── annotations.scala
│ │ ├── companions.scala
│ │ ├── implicits.scala
│ │ ├── openapi
│ │ ├── GeneratedSchemaName.scala
│ │ ├── OpenApi.scala
│ │ ├── OpenApiMetadata.scala
│ │ ├── RestSchema.scala
│ │ ├── RestStructure.scala
│ │ ├── WhenAbsentInfo.scala
│ │ └── adjusters
│ │ │ └── Adjuster.scala
│ │ ├── raw
│ │ ├── AbstractMapping.scala
│ │ ├── HttpBody.scala
│ │ ├── JsonValue.scala
│ │ ├── PlainValue.scala
│ │ ├── RawRest.scala
│ │ ├── RestMetadata.scala
│ │ ├── RestRequest.scala
│ │ └── RestResponse.scala
│ │ └── util
│ │ └── WithHeaders.scala
│ └── test
│ └── scala
│ └── io
│ └── udash
│ └── rest
│ ├── CirceRestApiTest.scala
│ ├── CompilationErrorsTest.scala
│ ├── PolyRestApi.scala
│ ├── PolyRestDataCompanion.scala
│ ├── RestApiTest.scala
│ ├── RestTestApi.scala
│ ├── RestValidationTest.scala
│ ├── SomeServerApiImpl.scala
│ ├── TestRESTRecord.scala
│ ├── WriteOpenApi.scala
│ ├── openapi
│ ├── RestSchemaTest.scala
│ ├── customWa.scala
│ └── openapiDependencies.scala
│ └── raw
│ ├── MappingTest.scala
│ ├── RawRestTest.scala
│ └── ServerImplApiTest.scala
├── rpc
├── .js
│ └── src
│ │ ├── main
│ │ └── scala
│ │ │ └── io
│ │ │ └── udash
│ │ │ ├── rpc
│ │ │ ├── ConnectionStatus.scala
│ │ │ ├── DefaultServerRPC.scala
│ │ │ ├── RPCFrontend.scala
│ │ │ ├── internals
│ │ │ │ ├── ExposesClientRPC.scala
│ │ │ │ ├── ServerConnector.scala
│ │ │ │ └── UsesServerRPC.scala
│ │ │ ├── package.scala
│ │ │ └── serialization
│ │ │ │ └── DefaultExceptionCodecRegistry.scala
│ │ │ └── wrappers
│ │ │ └── atmosphere
│ │ │ └── Atmosphere.scala
│ │ └── test
│ │ └── scala
│ │ └── io
│ │ └── udash
│ │ └── rpc
│ │ ├── NativeRpcMessagesTest.scala
│ │ ├── ServerRPCTest.scala
│ │ └── internals
│ │ └── ExposesClientRPCTest.scala
├── .jvm
│ └── src
│ │ ├── main
│ │ └── scala
│ │ │ └── io
│ │ │ └── udash
│ │ │ └── rpc
│ │ │ ├── AtmosphereService.scala
│ │ │ ├── DefaultAtmosphereServiceConfig.scala
│ │ │ ├── DefaultClientRPC.scala
│ │ │ ├── ExposesServerRPC.scala
│ │ │ ├── RPCBackend.scala
│ │ │ ├── RpcServlet.scala
│ │ │ ├── internals
│ │ │ ├── BroadcastManager.scala
│ │ │ └── UsesClientRPC.scala
│ │ │ ├── package.scala
│ │ │ ├── serialization
│ │ │ └── DefaultExceptionCodecRegistry.scala
│ │ │ └── utils
│ │ │ ├── CallLogging.scala
│ │ │ ├── DefaultAtmosphereFramework.scala
│ │ │ ├── FileDownloadServlet.scala
│ │ │ └── FileUploadServlet.scala
│ │ └── test
│ │ └── scala
│ │ └── io
│ │ └── udash
│ │ ├── rpc
│ │ ├── ClientRPCTest.scala
│ │ ├── DefaultAtmosphereServiceConfigTest.scala
│ │ ├── DefaultExceptionCodecRegistryTest.scala
│ │ ├── JVMSerializationIntegrationTest.scala
│ │ ├── RpcMessagesTest.scala
│ │ └── internals
│ │ │ ├── AtmosphereServiceTest.scala
│ │ │ └── ExposesServerRPCTest.scala
│ │ └── testing
│ │ └── UdashRpcBackendTest.scala
└── src
│ ├── main
│ └── scala
│ │ └── io
│ │ └── udash
│ │ └── rpc
│ │ ├── ExposesLocalRPC.scala
│ │ ├── UsesRemoteRPC.scala
│ │ ├── companions.scala
│ │ ├── rawrpc.scala
│ │ ├── serialization
│ │ ├── DefaultUdashSerialization.scala
│ │ ├── EscapeUtils.scala
│ │ └── ExceptionCodecRegistry.scala
│ │ └── utils
│ │ └── Logged.scala
│ └── test
│ └── scala
│ └── io
│ └── udash
│ └── rpc
│ ├── RpcMessagesTest.scala
│ ├── SerializationIntegrationTestBase.scala
│ ├── TestRPC.scala
│ ├── Utils.scala
│ └── types.scala
├── scripts
└── authors.scala
└── utils
├── .js
└── src
│ ├── main
│ └── scala
│ │ └── io
│ │ └── udash
│ │ ├── logging
│ │ └── UdashLogger.scala
│ │ └── utils
│ │ ├── CrossCollections.scala
│ │ └── URLEncoder.scala
│ └── test
│ └── scala
│ └── io
│ └── udash
│ └── testing
│ ├── AsyncUdashSharedTest.scala
│ └── UdashFrontendTest.scala
├── .jvm
└── src
│ ├── main
│ └── scala
│ │ └── io
│ │ └── udash
│ │ ├── logging
│ │ └── UdashLogger.scala
│ │ └── utils
│ │ ├── CrossCollections.scala
│ │ └── URLEncoder.scala
│ └── test
│ └── scala
│ └── io
│ └── udash
│ └── testing
│ └── AsyncUdashSharedTest.scala
└── src
├── main
└── scala
│ └── io
│ └── udash
│ ├── logging
│ ├── CrossLogger.scala
│ └── CrossLogging.scala
│ └── utils
│ ├── CallbacksHandler.scala
│ ├── FilteringUtils.scala
│ └── Registration.scala
└── test
└── scala
└── io
└── udash
├── testing
├── AsyncUdashSharedTestBase.scala
├── CompilationErrorAssertions.scala
└── UdashSharedTest.scala
└── utils
├── CallbacksHandlerTest.scala
├── FilteringUtilsTest.scala
└── URLEncoderTest.scala
/.github/workflows/docker.yml:
--------------------------------------------------------------------------------
1 | name: Create and publish a Docker image
2 |
3 | on:
4 | push:
5 | tags: [ "v[0-9]+*" ]
6 |
7 | env:
8 | REGISTRY: ghcr.io
9 |
10 | jobs:
11 | build-and-push-image:
12 | runs-on: ubuntu-22.04 # https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2204-Readme.md
13 | # only run on tag push
14 | if: github.event_name == 'push' && (startsWith(github.ref, 'refs/tags/v'))
15 | permissions:
16 | contents: read
17 | packages: write
18 | steps:
19 | - uses: actions/checkout@v4
20 | - uses: docker/login-action@v3
21 | with:
22 | registry: ${{ env.REGISTRY }}
23 | username: udashframework
24 | password: ${{ secrets.GITHUB_TOKEN }}
25 | - uses: actions/setup-java@v4
26 | with:
27 | distribution: temurin
28 | java-version: 17
29 | cache: sbt
30 | - name: Get version
31 | id: get_tag_name
32 | run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
33 | - name: Build and push Docker image
34 | run: >
35 | sbt
36 | 'set ThisBuild/version := "${{ steps.get_tag_name.outputs.VERSION }}"'
37 | 'project guide-packager'
38 | 'set dockerRepository := Some("${{ env.REGISTRY }}")'
39 | 'set dockerUsername := Some("udashframework")'
40 | docker:publish
41 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.scala-steward.conf:
--------------------------------------------------------------------------------
1 | updates.ignore = [
2 | {groupId = "com.vladsch.flexmark", artifactId = "flexmark-all"},
3 | ]
4 | updates.pin = [
5 | {groupId = "ch.qos.logback", version = "1.3."},
6 | ]
7 | pullRequests.grouping = [
8 | {
9 | name = "jetty",
10 | title = "Update Jetty dependencies",
11 | filter = [{ group = "org.eclipse.jetty" }, { group = "org.eclipse.jetty*" }]
12 | }
13 | ]
14 |
--------------------------------------------------------------------------------
/auth/.js/src/main/scala/io/udash/auth/AuthApplication.scala:
--------------------------------------------------------------------------------
1 | package io.udash.auth
2 |
3 | import io.udash._
4 |
5 | object AuthApplication {
6 | implicit class ApplicationAuthExt[HierarchyRoot >: Null <: GState[HierarchyRoot]](val application: Application[HierarchyRoot]) extends AnyVal {
7 | /**
8 | * Adds the default listener of authorization failure in routing (redirects to provided state).
9 | *
10 | * @param authFailedRedirectState application will redirect user to this state after auth fail
11 | */
12 | def withDefaultRoutingFailureListener(authFailedRedirectState: HierarchyRoot): Application[HierarchyRoot] = {
13 | application.onRoutingFailure {
14 | case _: UnauthorizedException | _: UnauthenticatedException if application.currentState != authFailedRedirectState =>
15 | application.goTo(authFailedRedirectState)
16 | }
17 | application
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/auth/.js/src/main/scala/io/udash/auth/AuthPresenter.scala:
--------------------------------------------------------------------------------
1 | package io.udash.auth
2 |
3 | import io.udash._
4 |
5 | /**
6 | * Presenter which check user access in `handleState` method.
7 | *
8 | * @param permission PermissionCombinator verified against provided `userCtx`.
9 | * @param requireAuthenticated If `true`, the presenter requires `userCtx` to don't be Unauthenticated subclass.
10 | */
11 | abstract class AuthPresenter[S <: State](permission: PermissionCombinator, requireAuthenticated: Boolean = false)
12 | (implicit userCtx: UserCtx)
13 | extends Presenter[S] with AuthRequires {
14 |
15 | override def handleState(state: S): Unit = {
16 | require(permission, requireAuthenticated)
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/auth/.js/src/test/scala/io/udash/auth/AuthFrontendTestUtils.scala:
--------------------------------------------------------------------------------
1 | package io.udash.auth
2 |
3 | import io.udash.State
4 |
5 | trait AuthFrontendTestUtils {
6 | sealed trait TestStates extends State {
7 | override type HierarchyRoot = TestStates
8 | override def parentState: Option[HierarchyRoot] = None
9 | }
10 | case object SomeState extends TestStates
11 | case object SecondState extends TestStates
12 | case object ThirdState extends TestStates
13 | }
14 |
--------------------------------------------------------------------------------
/auth/.js/src/test/scala/io/udash/auth/AuthPresenterTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash.auth
2 |
3 | import io.udash.testing.UdashFrontendTest
4 |
5 | class AuthPresenterTest extends UdashFrontendTest with AuthTestUtils with AuthFrontendTestUtils {
6 | import PermissionCombinator.AllowAll
7 |
8 | "AuthPresenter" should {
9 | "throw an exception if user is not authenticated" in {
10 | class SomePresenter extends AuthPresenter[SomeState.type](AllowAll, requireAuthenticated = false)(UnauthenticatedUser)
11 |
12 | val p = new SomePresenter
13 | p.handleState(SomeState)
14 |
15 | class SomePresenter2 extends AuthPresenter[SomeState.type](AllowAll, requireAuthenticated = true)(UnauthenticatedUser)
16 |
17 | val p2 = new SomePresenter2
18 | intercept[UnauthenticatedException] {
19 | p2.handleState(SomeState)
20 | }
21 | }
22 |
23 | "throw an exception if user is not authorized" in {
24 | class SomePresenter extends AuthPresenter[SomeState.type](P1.and(P2.or(P3)))(User(Set(P1, P2)))
25 |
26 | val p = new SomePresenter
27 | p.handleState(SomeState)
28 |
29 | class SomePresenter2 extends AuthPresenter[SomeState.type](P1.and(P2.or(P3)))(User(Set(P1)))
30 |
31 | val p2 = new SomePresenter2
32 | intercept[UnauthorizedException] {
33 | p2.handleState(SomeState)
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/auth/src/main/scala/io/udash/auth/AuthRequires.scala:
--------------------------------------------------------------------------------
1 | package io.udash.auth
2 |
3 | trait AuthRequires {
4 | /** Checks if user context has required permissions. */
5 | def require(permission: PermissionCombinator, requireAuthenticated: Boolean = false)(implicit userCtx: UserCtx): Unit = {
6 | if (requireAuthenticated && !userCtx.isAuthenticated) throw new UnauthenticatedException()
7 | if (!permission.check(userCtx)) throw new UnauthorizedException()
8 | }
9 |
10 | /** Checks if user is authenticated. */
11 | def requireAuthenticated()(implicit userCtx: UserCtx): Unit =
12 | require(PermissionCombinator.AllowAll, requireAuthenticated = true)
13 | }
14 |
15 | object AuthRequires extends AuthRequires
16 |
--------------------------------------------------------------------------------
/auth/src/main/scala/io/udash/auth/DefaultAuthExceptionCodecRegistry.scala:
--------------------------------------------------------------------------------
1 | package io.udash.auth
2 |
3 | import com.avsystem.commons.serialization.GenCodec
4 | import io.udash.rpc.serialization.DefaultExceptionCodecRegistry
5 |
6 | class DefaultAuthExceptionCodecRegistry extends DefaultExceptionCodecRegistry {
7 | register(GenCodec.create(_ => new UnauthenticatedException(), exceptionWriter))
8 | register(GenCodec.create(_ => new UnauthorizedException(), exceptionWriter))
9 | }
10 |
--------------------------------------------------------------------------------
/auth/src/main/scala/io/udash/auth/Permission.scala:
--------------------------------------------------------------------------------
1 | package io.udash.auth
2 |
3 | /**
4 | * Base class for permissions used with PermissionControl. Permissions are compared by ID.
5 | */
6 | trait Permission {
7 | def id: PermissionId
8 |
9 | override def equals(other: Any): Boolean = other match {
10 | case that: Permission => id == that.id
11 | case _ => false
12 | }
13 |
14 | override def hashCode(): Int =
15 | id.hashCode()
16 |
17 | override def toString =
18 | s"Permission(${id.value}"
19 | }
20 |
21 | object Permission {
22 | /** Single permission as a combinator resolved implicitly. */
23 | implicit class Single(private val permission: Permission) extends AnyVal with PermissionCombinator {
24 | override def check(ctx: UserCtx): Boolean =
25 | ctx.has(permission)
26 |
27 | override def toString: String =
28 | permission.toString
29 | }
30 | }
--------------------------------------------------------------------------------
/auth/src/main/scala/io/udash/auth/PermissionId.scala:
--------------------------------------------------------------------------------
1 | package io.udash.auth
2 |
3 | import com.avsystem.commons.serialization.{HasGenCodec, transparent}
4 |
5 | @transparent
6 | case class PermissionId(value: String) extends AnyVal
7 | object PermissionId extends HasGenCodec[PermissionId]
8 |
--------------------------------------------------------------------------------
/auth/src/main/scala/io/udash/auth/UserCtx.scala:
--------------------------------------------------------------------------------
1 | package io.udash.auth
2 |
3 | trait UserCtx {
4 | def has(permission: Permission): Boolean
5 | def isAuthenticated: Boolean
6 | }
7 |
8 | object UserCtx {
9 | trait Unauthenticated extends UserCtx {
10 | override def has(permission: Permission): Boolean = false
11 | override def isAuthenticated: Boolean = false
12 | }
13 | }
--------------------------------------------------------------------------------
/auth/src/main/scala/io/udash/auth/exceptions.scala:
--------------------------------------------------------------------------------
1 | package io.udash.auth
2 |
3 | import com.avsystem.commons.serialization.HasGenCodec
4 |
5 | case class UnauthenticatedException() extends RuntimeException(s"User has to be authenticated to access this content.")
6 | object UnauthenticatedException extends HasGenCodec[UnauthenticatedException]
7 |
8 | case class UnauthorizedException() extends RuntimeException(s"Provided user context does not have access to this content.")
9 | object UnauthorizedException extends HasGenCodec[UnauthorizedException]
10 |
--------------------------------------------------------------------------------
/auth/src/test/scala/io/udash/auth/AuthRequiresTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash.auth
2 |
3 | import io.udash.testing.UdashSharedTest
4 |
5 | class AuthRequiresTest extends UdashSharedTest with AuthTestUtils {
6 | import PermissionCombinator.AllowAll
7 |
8 | "AuthRequires utils" should {
9 | "check user's permissions" in {
10 | implicit val user: UserCtx = User(Set(P1, P2))
11 |
12 | AuthRequires.require(P1.and(P2))
13 | intercept[UnauthorizedException] { AuthRequires.require(P1.and(P3)) }
14 | intercept[UnauthorizedException] { AuthRequires.require(P2.and(P3)) }
15 | AuthRequires.require(P1.or(P3))
16 | AuthRequires.require(AllowAll)
17 | }
18 |
19 | "check is user is authenticated" in {
20 | implicit val user: UserCtx = UnauthenticatedUser
21 |
22 | AuthRequires.require(AllowAll)
23 | intercept[UnauthenticatedException] { AuthRequires.require(AllowAll, requireAuthenticated = true) }
24 | intercept[UnauthenticatedException] { AuthRequires.requireAuthenticated() }
25 | intercept[UnauthenticatedException] { AuthRequires.require(P2.and(P3), requireAuthenticated = true) }
26 | intercept[UnauthorizedException] { AuthRequires.require(P2.and(P3)) }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/auth/src/test/scala/io/udash/auth/AuthTestUtils.scala:
--------------------------------------------------------------------------------
1 | package io.udash.auth
2 |
3 | trait AuthTestUtils {
4 |
5 | class Perm(override val id: PermissionId) extends Permission
6 |
7 | case object P1 extends Perm(PermissionId("1"))
8 | case object P2 extends Perm(PermissionId("2"))
9 | case object P3 extends Perm(PermissionId("3"))
10 |
11 | case class User(perms: Set[Permission]) extends UserCtx {
12 | override def has(permission: Permission): Boolean =
13 | perms.contains(permission)
14 |
15 | override def isAuthenticated: Boolean = true
16 | }
17 |
18 | case class UnauthenticatedUserWithPerms(perms: Set[Permission]) extends UserCtx {
19 | override def has(permission: Permission): Boolean =
20 | perms.contains(permission)
21 |
22 | override def isAuthenticated: Boolean = false
23 | }
24 |
25 | case object UnauthenticatedUser extends UserCtx.Unauthenticated
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/benchmarks/.js/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Udash Benchmarks
5 |
6 |
7 | Loading...
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/benchmarks/.js/src/main/scala/io/udash/benchmarks/Main.scala:
--------------------------------------------------------------------------------
1 | package io.udash.benchmarks
2 |
3 | import io.udash.benchmarks.css.CssStylesApply
4 | import io.udash.benchmarks.i18n.StaticTranslationBinding
5 | import io.udash.benchmarks.properties._
6 | import japgolly.scalajs.benchmark.engine.EngineOptions
7 | import japgolly.scalajs.benchmark.gui.BenchmarkGUI
8 | import org.scalajs.dom.document
9 |
10 | object Main {
11 | def main(args: Array[String]): Unit = {
12 | BenchmarkGUI.renderMenu(document.getElementById("body"), engineOptions = EngineOptions.default.copy(iterations = 3))(
13 | SinglePropertyListeners.suite,
14 | ModelPropertyListeners.suite,
15 | ModelPropertyWithSeqListeners.suite,
16 | TransformedSeqPropertyListeners.suite,
17 | FilteredSeqPropertyListeners.suite,
18 | ReversedSeqPropertyListeners.suite,
19 | ZippedSeqPropertyListeners.suite,
20 |
21 | StaticTranslationBinding.suite,
22 |
23 | CssStylesApply.suite,
24 |
25 | PropertyParameters.suite
26 | )
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/benchmarks/.js/src/main/scala/io/udash/benchmarks/css/CssStylesApply.scala:
--------------------------------------------------------------------------------
1 | package io.udash.benchmarks.css
2 |
3 | import io.udash.css.{CssStyleName, CssView}
4 | import japgolly.scalajs.benchmark._
5 | import japgolly.scalajs.benchmark.gui._
6 | import scalatags.JsDom.all._
7 |
8 | object CssStylesApply extends CssView {
9 |
10 | val styles = Benchmark("three styles") {
11 | div(
12 | CssStyleName("style-1"),
13 | CssStyleName("style-2"),
14 | CssStyleName("style-3"),
15 | CssStyleName("style-4"),
16 | CssStyleName("style-5"),
17 | CssStyleName("style-6"),
18 | Seq(CssStyleName("style-12"), CssStyleName("style-13"), CssStyleName("style-14")),
19 | Seq(CssStyleName("style-22"), CssStyleName("style-23"), CssStyleName("style-24")),
20 | Seq(CssStyleName("style-32"), CssStyleName("style-33"), CssStyleName("style-34")),
21 | Seq(CssStyleName("style-42"), CssStyleName("style-43"), CssStyleName("style-44")),
22 | Seq(CssStyleName("style-52"), CssStyleName("style-53"), CssStyleName("style-54"))
23 | ).render
24 | }
25 |
26 | val suite = GuiSuite(
27 | Suite("CssStyle apply")(
28 | styles
29 | )
30 | )
31 | }
32 |
--------------------------------------------------------------------------------
/benchmarks/.js/src/main/scala/io/udash/benchmarks/i18n/StaticTranslationBinding.scala:
--------------------------------------------------------------------------------
1 | package io.udash.benchmarks.i18n
2 |
3 | import io.udash.i18n._
4 | import japgolly.scalajs.benchmark._
5 | import japgolly.scalajs.benchmark.gui._
6 |
7 | import scala.concurrent.Future
8 | import scalatags.JsDom.all._
9 |
10 | object StaticTranslationBinding {
11 |
12 | val instantSuccessTranslations = Benchmark("instant success translation") {
13 | div(
14 | (1 to 50).map { _ =>
15 | span(
16 | translatedAttr(Future.successful(Translated("Test")), "data-test"),
17 | translated(Future.successful(Translated("Test")))
18 | ).render
19 | }
20 | ).render
21 | }
22 |
23 | val futureTranslations = Benchmark("future translation") {
24 | import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue
25 | div(
26 | (1 to 50).map { _ =>
27 | span(
28 | translatedAttr(Future(Translated("Test")), "data-test"),
29 | translated(Future(Translated("Test")))
30 | ).render
31 | }
32 | ).render
33 | }
34 |
35 | val suite = GuiSuite(
36 | Suite("StaticTranslations")(
37 | instantSuccessTranslations,
38 | futureTranslations
39 | )
40 | )
41 | }
42 |
--------------------------------------------------------------------------------
/benchmarks/.js/src/main/scala/io/udash/benchmarks/properties/ReversedSeqPropertyListeners.scala:
--------------------------------------------------------------------------------
1 | package io.udash.benchmarks.properties
2 |
3 | import io.udash._
4 | import japgolly.scalajs.benchmark._
5 | import japgolly.scalajs.benchmark.gui._
6 |
7 | object ReversedSeqPropertyListeners extends BenchmarkUtils {
8 | private val seqSize = 50
9 | private val properties: Seq[(String, () => (SeqProperty[Int], ReadableSeqProperty[Int]))] = Seq(
10 | ("direct property", () => {
11 | val p = SeqProperty(Seq.tabulate(seqSize)(identity))
12 | (p, p)
13 | }),
14 | ("reversed elements", () => {
15 | val p = SeqProperty(Seq.tabulate(seqSize)(identity))
16 | val t = p.reversed()
17 | (p, t)
18 | })
19 | )
20 |
21 | private val benchmarks = generateGetSetListenBenchmarks[SeqProperty[Int], ReadableSeqProperty[Int]](properties)(
22 | Seq(20), Seq(0.1, 1, 10), Seq(0, 1, 10, 100),
23 | Seq(
24 | ("whole Seq set", (p, i) => p.set(Seq.tabulate(seqSize)(_ + i)), _.get),
25 | ("replace part of Seq", replaceElements, _.get)
26 | ),
27 | Seq(("empty listener", _.listen(_ => ())))
28 | )
29 |
30 | val suite = GuiSuite(
31 | Suite("SeqProperty - reverse - set, get & listen")(benchmarks: _*)
32 | )
33 | }
34 |
--------------------------------------------------------------------------------
/benchmarks/.js/src/main/scala/io/udash/benchmarks/properties/ZippedSeqPropertyListeners.scala:
--------------------------------------------------------------------------------
1 | package io.udash.benchmarks.properties
2 |
3 | import io.udash._
4 | import japgolly.scalajs.benchmark._
5 | import japgolly.scalajs.benchmark.gui._
6 |
7 | object ZippedSeqPropertyListeners extends BenchmarkUtils {
8 | private val seqSize = 50
9 | private val properties: Seq[(String, () => (SeqProperty[Int], ReadableSeqProperty[Int]))] = Seq(
10 | ("direct property", () => {
11 | val p = SeqProperty(Seq.tabulate(seqSize)(identity))
12 | (p, p)
13 | }),
14 | ("zipped elements", () => {
15 | val p = SeqProperty(Seq.tabulate(seqSize)(identity))
16 | val t = p.zip(p)(_ + _)
17 | (p, t)
18 | })
19 | )
20 |
21 | private val benchmarks = generateGetSetListenBenchmarks[SeqProperty[Int], ReadableSeqProperty[Int]](properties)(
22 | Seq(20), Seq(0.1, 1, 10), Seq(0, 1, 10, 100),
23 | Seq(
24 | ("whole Seq set", (p, i) => p.set(Seq.tabulate(seqSize)(_ + i)), _.get),
25 | ("replace part of Seq", replaceElements, _.get)
26 | ),
27 | Seq(("empty listener", _.listen(_ => ())))
28 | )
29 |
30 | val suite = GuiSuite(
31 | Suite("SeqProperty - zipped - set, get & listen")(benchmarks: _*)
32 | )
33 | }
34 |
--------------------------------------------------------------------------------
/bootstrap4/.js/src/main/scala/io/udash/bootstrap/UdashBootstrap.scala:
--------------------------------------------------------------------------------
1 | package io.udash.bootstrap
2 |
3 | import io.udash._
4 | import org.scalajs.dom.Element
5 | import scalatags.JsDom.all._
6 |
7 | object UdashBootstrap {
8 | final val False: ReadableProperty[Boolean] = false.toProperty
9 | final val True: ReadableProperty[Boolean] = true.toProperty
10 | final val ColorSecondary: ReadableProperty[BootstrapStyles.Color] = BootstrapStyles.Color.Secondary.toProperty
11 | private final val NoneProperty = scala.None.toProperty
12 | def None[A]: ReadableProperty[Option[A]] = NoneProperty
13 |
14 | /** Loads FontAwesome styles. */
15 | def loadFontAwesome(): Element =
16 | link(rel := "stylesheet", href := "https://use.fontawesome.com/releases/v5.10.1/css/all.css").render
17 |
18 | /** Loads Bootstrap styles. */
19 | def loadBootstrapStyles(): Element =
20 | link(rel := "stylesheet", href := "https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css").render
21 | }
22 |
--------------------------------------------------------------------------------
/bootstrap4/.js/src/main/scala/io/udash/bootstrap/alert/UdashAlert.scala:
--------------------------------------------------------------------------------
1 | package io.udash.bootstrap
2 | package alert
3 |
4 | import io.udash._
5 | import io.udash.bindings.modifiers.Binding
6 | import io.udash.bootstrap.utils.BootstrapStyles
7 | import org.scalajs.dom.Element
8 | import scalatags.JsDom.all._
9 |
10 | final class UdashAlert private[alert](
11 | alertStyle: ReadableProperty[BootstrapStyles.Color], override val componentId: ComponentId
12 | )(content: Binding.NestedInterceptor => Modifier) extends UdashAlertBase(alertStyle, componentId) {
13 | override val render: Element =
14 | template(content(nestedInterceptor)).render
15 | }
16 |
17 | object UdashAlert extends UdashAlertBaseCompanion[UdashAlert] {
18 | protected def create(alertStyle: ReadableProperty[BootstrapStyles.Color], componentId: ComponentId)(
19 | content: Binding.NestedInterceptor => Modifier
20 | ): UdashAlert = {
21 | new UdashAlert(alertStyle, componentId)(content)
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/bootstrap4/.js/src/main/scala/io/udash/bootstrap/package.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 |
3 | import io.udash.bootstrap.utils.BootstrapImplicits
4 | import io.udash.component.Components
5 |
6 | package object bootstrap extends BootstrapImplicits with Components {
7 | final val BootstrapStyles = io.udash.bootstrap.utils.BootstrapStyles
8 | final val BootstrapTags = io.udash.bootstrap.utils.BootstrapTags
9 | }
10 |
--------------------------------------------------------------------------------
/bootstrap4/.js/src/main/scala/io/udash/bootstrap/utils/BootstrapImplicits.scala:
--------------------------------------------------------------------------------
1 | package io.udash.bootstrap.utils
2 |
3 | import io.udash.Url
4 | import io.udash.bindings.modifiers.Binding
5 | import org.scalajs.dom.Element
6 | import scalatags.JsDom
7 | import scalatags.JsDom.GenericAttr
8 | import scalatags.JsDom.all._
9 |
10 | trait BootstrapImplicits {
11 | implicit val urlAttrValue: AttrValue[Url] = (t: Element, a: JsDom.Attr, v: Url) => new GenericAttr[String].apply(t, a, v.value)
12 |
13 | implicit def withoutNested(modifier: Modifier): Binding.NestedInterceptor => Modifier = _ => modifier
14 | implicit def stringWithoutNested(modifier: String): Binding.NestedInterceptor => Modifier = _ => modifier
15 | }
16 |
17 | object BootstrapImplicits extends BootstrapImplicits
--------------------------------------------------------------------------------
/bootstrap4/.js/src/main/scala/io/udash/bootstrap/utils/BootstrapTags.scala:
--------------------------------------------------------------------------------
1 | package io.udash.bootstrap.utils
2 |
3 | object BootstrapTags {
4 | import scalatags.JsDom.all._
5 |
6 | final val dataBackdrop = data("backdrop")
7 | final val dataBind = data("bind")
8 | final val dataContent = data("content")
9 | final val dataDismiss = data("dismiss")
10 | final val dataKeyboard = data("keyboard")
11 | final val dataLabel = data("label")
12 | final val dataParent = data("parent")
13 | final val dataOriginalTitle = data("original-title")
14 | final val dataRide = data("ride")
15 | final val dataShow = data("show")
16 | final val dataSlide = data("slide")
17 | final val dataSlideTo = data("slide-to")
18 | final val dataTarget = data("target")
19 | final val dataToggle = data("toggle")
20 | }
21 |
--------------------------------------------------------------------------------
/bootstrap4/.js/src/main/scala/io/udash/bootstrap/utils/UdashBootstrapComponent.scala:
--------------------------------------------------------------------------------
1 | package io.udash.bootstrap.utils
2 |
3 | import io.udash.bindings.modifiers.Binding
4 | import io.udash.component.Component
5 | import io.udash.css.CssView
6 | import io.udash.wrappers.jquery.*
7 | import org.scalajs.dom.Element
8 |
9 | /** Base trait for Bootstrap components. */
10 | trait UdashBootstrapComponent extends Component with CssView {
11 | override val render: Element
12 |
13 | protected class JQueryOnBinding(selector: JQuery, event: EventName, callback: JQueryCallback) extends Binding {
14 | selector.on(event, callback)
15 |
16 | override def kill(): Unit = {
17 | super.kill()
18 | selector.off(event, callback)
19 | }
20 |
21 | override def applyTo(t: Element): Unit = ()
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/bootstrap4/.js/src/test/scala/io/udash/bootstrap/jumbotron/UdashJumbotronTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash.bootstrap.jumbotron
2 |
3 | import io.udash._
4 | import io.udash.bootstrap._
5 | import io.udash.bootstrap.utils.BootstrapStyles
6 | import io.udash.testing.UdashCoreFrontendTest
7 | import scalatags.JsDom.all._
8 |
9 | class UdashJumbotronTest extends UdashCoreFrontendTest {
10 |
11 | "UdashJumbotron component" should {
12 | "clean up property listeners" in {
13 | val fluid = Property[Boolean](false)
14 | val jumbo = UdashJumbotron(fluid)(
15 | h4("Content")
16 | )
17 | val el = jumbo.render
18 | el.childNodes.length should be(1)
19 | el.textContent should be("Content")
20 |
21 | el.classList shouldNot contain(BootstrapStyles.Jumbotron.fluid.className)
22 |
23 | fluid.set(true)
24 | el.classList should contain(BootstrapStyles.Jumbotron.fluid.className)
25 |
26 | jumbo.kill()
27 | fluid.listenersCount() should be(0)
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/bootstrap4/.js/src/test/scala/io/udash/bootstrap/tooltip/PopoverTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash.bootstrap.tooltip
2 |
3 | class PopoverTest extends TooltipTestUtils {
4 | "Popover" should tooltipTest(UdashPopover, expectContent = true)
5 | }
6 |
--------------------------------------------------------------------------------
/bootstrap4/.js/src/test/scala/io/udash/bootstrap/tooltip/TooltipTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash.bootstrap.tooltip
2 |
3 | class TooltipTest extends TooltipTestUtils {
4 | "Tooltip" should tooltipTest(UdashTooltip, expectContent = false)
5 | }
6 |
--------------------------------------------------------------------------------
/core/.js/src/main/scala/io/udash/bindings/inputs/Checkbox.scala:
--------------------------------------------------------------------------------
1 | package io.udash.bindings.inputs
2 |
3 | import io.udash._
4 | import org.scalajs.dom.Event
5 | import org.scalajs.dom.html.{Input => JSInput}
6 | import scalatags.JsDom.all._
7 |
8 | /**
9 | * Plain checkbox bidirectionally bound to Property.
10 | *
11 | * For SeqProperty take a look at [[io.udash.bindings.inputs.CheckButtons]]
12 | */
13 | object Checkbox {
14 | /**
15 | * @param selected Property to bind.
16 | * @param inputModifiers Additional Modifiers, don't use modifiers on type, checked and onchange attributes.
17 | * @return HTML input (checkbox) tag with bound Property and applied modifiers.
18 | */
19 | def apply(selected: Property[Boolean])(inputModifiers: Modifier*): InputBinding[JSInput] = {
20 | new InputBinding[JSInput] {
21 | private val in = input(inputModifiers, tpe := "checkbox").render
22 |
23 | propertyListeners += selected.listen(in.checked = _, initUpdate = true)
24 | in.onchange = (_: Event) => selected.set(in.checked)
25 |
26 | override def render: JSInput = in
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/core/.js/src/main/scala/io/udash/bindings/inputs/InputBinding.scala:
--------------------------------------------------------------------------------
1 | package io.udash.bindings.inputs
2 |
3 | import io.udash.bindings.modifiers.Binding
4 | import org.scalajs.dom.Element
5 |
6 | trait InputBinding[RenderType <: Element] extends Binding {
7 | def render: RenderType
8 |
9 | override def applyTo(t: Element): Unit = {
10 | t.appendChild(render)
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/core/.js/src/main/scala/io/udash/bindings/inputs/SelectBinding.scala:
--------------------------------------------------------------------------------
1 | package io.udash.bindings.inputs
2 |
3 | import io.udash._
4 | import org.scalajs.dom.Event
5 | import org.scalajs.dom.html.Select
6 | import scalatags.JsDom.all._
7 |
8 | private[inputs] class SelectBinding[T](
9 | options: ReadableSeqProperty[T], label: T => Modifier, labelNoValue: Option[Modifier], selectModifiers: Modifier*
10 | )(
11 | checkedIf: T => ReadableProperty[Boolean],
12 | refreshSelection: Seq[T] => Unit,
13 | onChange: Select => Event => Unit
14 | ) extends InputBinding[Select] {
15 | private val selector = select(selectModifiers)(
16 | produce(options) { opts =>
17 | kill()
18 | refreshSelection(opts)
19 |
20 | val empty = labelNoValue.map { l =>
21 | option(l, value := "").render
22 | }
23 |
24 | {
25 | empty.iterator ++ opts.iterator.zipWithIndex.map { case (opt, idx) =>
26 | val el = option(value := idx.toString, label(opt)).render
27 |
28 | val selected = checkedIf(opt)
29 | propertyListeners += selected.listen(el.selected = _, initUpdate = true)
30 | el
31 | }
32 | }.toSeq
33 | }
34 | ).render
35 |
36 | selector.onchange = onChange(selector)
37 |
38 | override def render: Select = selector
39 | }
--------------------------------------------------------------------------------
/core/.js/src/main/scala/io/udash/bindings/modifiers/EmptyModifier.scala:
--------------------------------------------------------------------------------
1 | package io.udash.bindings.modifiers
2 |
3 | import scalatags.generic.Modifier
4 |
5 | private[udash] final class EmptyModifier[T] extends Modifier[T] {
6 | override def applyTo(t: T): Unit = ()
7 | }
--------------------------------------------------------------------------------
/core/.js/src/main/scala/io/udash/bindings/modifiers/PropertyModifier.scala:
--------------------------------------------------------------------------------
1 | package io.udash.bindings.modifiers
2 |
3 | import io.udash.properties.single.ReadableProperty
4 | import io.udash.utils.Registration
5 | import org.scalajs.dom.Node
6 |
7 | private[bindings] class PropertyModifier[T](
8 | override val property: ReadableProperty[T],
9 | override val builder: (T, Binding.NestedInterceptor) => Seq[Node],
10 | override val checkNull: Boolean,
11 | override val customElementsReplace: DOMManipulator.ReplaceMethod
12 | ) extends ValueModifier[T] {
13 |
14 | def this(property: ReadableProperty[T], builder: T => Seq[Node], checkNull: Boolean, customElementsReplace: DOMManipulator.ReplaceMethod) = {
15 | this(property, (data: T, _: Binding.NestedInterceptor) => builder(data), checkNull, customElementsReplace)
16 | }
17 |
18 | def listen(callback: T => Unit): Registration =
19 | property.listen(callback)
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/core/.js/src/main/scala/io/udash/bindings/modifiers/SeqAsValueModifier.scala:
--------------------------------------------------------------------------------
1 | package io.udash.bindings.modifiers
2 |
3 | import com.avsystem.commons._
4 | import io.udash.bindings.modifiers.Binding.NestedInterceptor
5 | import io.udash.properties.seq.ReadableSeqProperty
6 | import io.udash.properties.single.ReadableProperty
7 | import io.udash.utils.Registration
8 | import org.scalajs.dom.Node
9 |
10 | private[bindings] final class SeqAsValueModifier[T](
11 | override val property: ReadableSeqProperty[T, _ <: ReadableProperty[T]],
12 | build: (Seq[T], Binding.NestedInterceptor) => Seq[Node],
13 | override val customElementsReplace: DOMManipulator.ReplaceMethod
14 | ) extends ValueModifier[BSeq[T]] {
15 |
16 | override protected def builder: (BSeq[T], NestedInterceptor) => Seq[Node] = (data, interceptor) => build(data.toSeq, interceptor)
17 |
18 | def this(property: ReadableSeqProperty[T, _ <: ReadableProperty[T]], builder: Seq[T] => Seq[Node],
19 | customElementsReplace: DOMManipulator.ReplaceMethod) = {
20 | this(property, (data: Seq[T], _: Binding.NestedInterceptor) => builder(data), customElementsReplace)
21 | }
22 |
23 | override def listen(callback: BSeq[T] => Unit): Registration =
24 | property.listen(callback)
25 |
26 | override def checkNull: Boolean = false // SeqProperty can not return null from `get` method
27 |
28 | }
--------------------------------------------------------------------------------
/core/.js/src/main/scala/io/udash/bindings/modifiers/SeqPropertyModifier.scala:
--------------------------------------------------------------------------------
1 | package io.udash.bindings.modifiers
2 |
3 | import io.udash.properties.seq.ReadableSeqProperty
4 | import io.udash.properties.single.ReadableProperty
5 | import org.scalajs.dom._
6 |
7 | private[bindings] final class SeqPropertyModifier[T, E <: ReadableProperty[T]](
8 | override val property: ReadableSeqProperty[T, E],
9 | builder: (E, Binding.NestedInterceptor) => Seq[Node],
10 | override val customElementsReplace: DOMManipulator.ReplaceMethod,
11 | override val customElementsInsert: DOMManipulator.InsertMethod
12 | ) extends SeqPropertyModifierUtils[T, E] {
13 |
14 | def this(property: ReadableSeqProperty[T, E], builder: E => Seq[Node],
15 | customElementsReplace: DOMManipulator.ReplaceMethod,
16 | customElementsInsert: DOMManipulator.InsertMethod) = {
17 | this(property, (d, _) => builder(d), customElementsReplace, customElementsInsert)
18 | }
19 |
20 | protected def build(item: E): Seq[Node] =
21 | builder(item, propertyAwareNestedInterceptor(item))
22 | }
23 |
24 |
25 |
--------------------------------------------------------------------------------
/core/.js/src/main/scala/io/udash/bindings/modifiers/SimplePropertyModifier.scala:
--------------------------------------------------------------------------------
1 | package io.udash.bindings.modifiers
2 |
3 | import io.udash._
4 | import io.udash.properties.single.ReadableProperty
5 | import org.scalajs.dom
6 |
7 | private[bindings] final class SimplePropertyModifier(property: ReadableProperty[_]) extends PropertyModifier[Any](
8 | property,
9 | t => dom.document.createTextNode(t.toString),
10 | checkNull = true,
11 | DOMManipulator.DefaultElementReplace
12 | )
13 |
--------------------------------------------------------------------------------
/core/.js/src/main/scala/io/udash/bindings/modifiers/ValueModifier.scala:
--------------------------------------------------------------------------------
1 | package io.udash.bindings.modifiers
2 |
3 | import com.avsystem.commons._
4 | import io.udash.bindings._
5 | import io.udash.properties.single.ReadableProperty
6 | import io.udash.utils.Registration
7 | import org.scalajs.dom._
8 |
9 | private[bindings] trait ValueModifier[T] extends Binding with DOMManipulator {
10 |
11 | import Bindings._
12 |
13 | protected def property: ReadableProperty[T]
14 | protected def builder: (T, Binding.NestedInterceptor) => Seq[Node]
15 | protected def checkNull: Boolean
16 | protected def listen(callback: T => Unit): Registration
17 |
18 | override def applyTo(t: Element): Unit = {
19 | var elements: Seq[Node] = Seq.empty
20 |
21 | def rebuild(propertyValue: T): Unit = {
22 | killNestedBindings()
23 |
24 | val oldEls = elements
25 | val newEls: Seq[Node] =
26 | builder(propertyValue, nestedInterceptor)
27 | .optIf(!checkNull || propertyValue != null)
28 | .filter(_.nonEmpty)
29 | .getOrElse(emptyStringNode())
30 |
31 | elements = defragment(newEls)
32 |
33 | replace(t)(oldEls, elements)
34 | }
35 |
36 | propertyListeners.push(listen(rebuild))
37 | rebuild(property.get)
38 | }
39 | }
--------------------------------------------------------------------------------
/core/.js/src/main/scala/io/udash/bindings/modifiers/package.scala:
--------------------------------------------------------------------------------
1 | package io.udash.bindings
2 |
3 | import org.scalajs.dom.Node
4 |
5 | package object modifiers {
6 | implicit class ElementExts(private val el: Node) extends AnyVal {
7 | def replaceChildren(oldChildren: Seq[Node], newChildren: Seq[Node]): Unit = {
8 | if (oldChildren == null || oldChildren.isEmpty) newChildren.foreach(el.appendChild)
9 | else {
10 | oldChildren.iterator.zip(newChildren.iterator).foreach { case (old, fresh) =>
11 | el.replaceChild(fresh, old)
12 | }
13 | oldChildren.iterator.drop(newChildren.size).foreach(el.removeChild)
14 | newChildren.iterator.drop(oldChildren.size - 1).sliding(2)
15 | .foreach(s => if (s.size == 2) el.insertBefore(s(1), s(0).nextSibling))
16 | }
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/core/.js/src/main/scala/io/udash/component/Component.scala:
--------------------------------------------------------------------------------
1 | package io.udash.component
2 |
3 | import io.udash.bindings.modifiers.Binding
4 | import org.scalajs.dom.Element
5 |
6 | /** Base trait for Udash based components. */
7 | trait Component extends Binding {
8 | /** Component root DOM element ID. */
9 | val componentId: ComponentId
10 |
11 | /** Creates a component DOM hierarchy. */
12 | def render: Element
13 |
14 | override def applyTo(t: Element): Unit =
15 | t.appendChild(render)
16 | }
--------------------------------------------------------------------------------
/core/.js/src/main/scala/io/udash/component/Components.scala:
--------------------------------------------------------------------------------
1 | package io.udash.component
2 |
3 | trait Components {
4 | type Listenable = io.udash.component.Listenable
5 | type ListenableEvent = io.udash.component.ListenableEvent
6 | }
7 |
8 | object Components extends Components
9 |
--------------------------------------------------------------------------------
/core/.js/src/main/scala/io/udash/core/Defaults.scala:
--------------------------------------------------------------------------------
1 | package io.udash.core
2 |
3 | /**
4 | * Creates view with [[io.udash.core.EmptyPresenter]]. Used for static views.
5 | *
6 | * By default, instances of this class are compared by class name to prevent rerendering of static views.
7 | * This behaviour can be opted out of by overriding equals/hashCode.
8 | **/
9 | abstract class StaticViewFactory[S <: State](viewCreator: () => View) extends ViewFactory[S] {
10 | override def create(): (View, EmptyPresenter.type) =
11 | (viewCreator(), EmptyPresenter)
12 |
13 | override def equals(other: Any): Boolean = other != null && getClass.equals(other.getClass)
14 | override def hashCode(): Int = getClass.hashCode()
15 | }
16 |
17 | /** Ignores state changes. Useful for static views. */
18 | object EmptyPresenter extends Presenter[State] {
19 | override def handleState(state: State): Unit = ()
20 | }
--------------------------------------------------------------------------------
/core/.js/src/main/scala/io/udash/routing/Routing.scala:
--------------------------------------------------------------------------------
1 | package io.udash.routing
2 |
3 | trait Routing {
4 | type UrlChangeProvider = io.udash.routing.UrlChangeProvider
5 | type WindowUrlFragmentChangeProvider = io.udash.routing.WindowUrlFragmentChangeProvider
6 | type WindowUrlPathChangeProvider = io.udash.routing.WindowUrlPathChangeProvider
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/core/.js/src/main/scala/io/udash/routing/UrlLogging.scala:
--------------------------------------------------------------------------------
1 | package io.udash.routing
2 |
3 | import io.udash._
4 | import io.udash.logging.CrossLogging
5 |
6 | import scala.util.Try
7 |
8 | /**
9 | * RoutingRegistry mixin simplifying logging app navigation.
10 | */
11 | trait UrlLogging[S >: Null <: GState[S]] extends CrossLogging { app: Application[S] =>
12 | protected def log(url: String, referrer: Option[String]): Unit
13 |
14 | app.onStateChange(event =>
15 | Try(log(matchState(event.currentState).value, Try(matchState(event.oldState).value).toOption))
16 | .failed
17 | .foreach(t => logger.warn("Logging url change failed: {}", t.getMessage)))
18 | }
19 |
--------------------------------------------------------------------------------
/core/.js/src/test/scala/io/udash/routing/UrlLoggingTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash.routing
2 |
3 | import io.udash._
4 | import io.udash.core.Url
5 | import io.udash.testing._
6 |
7 | import scala.collection.mutable.ListBuffer
8 |
9 | class UrlLoggingTest extends AsyncUdashFrontendTest with TestRouting {
10 | "UrlLogging" should {
11 | "call logging impl on url change" in {
12 | val urlWithRef = ListBuffer.empty[(String, Option[String])]
13 |
14 | new TestViewFactory[TestState]: ViewFactory[_ <: TestState]
15 |
16 | initTestRouting(default = () => new TestViewFactory[TestState])
17 | val initUrl = Url("/")
18 | val urlProvider: TestUrlChangeProvider = new TestUrlChangeProvider(initUrl)
19 | val app = new Application[TestState](routing, vpRegistry, urlProvider) with UrlLogging[TestState] {
20 | override protected def log(url: String, referrer: Option[String]): Unit = {
21 | urlWithRef += ((url, referrer))
22 | }
23 | }
24 | app.run(emptyComponent())
25 |
26 | val urls = Seq("/", "/next", "/abc/1", "/next")
27 | val expected = (urls.head, Some("")) :: urls.sliding(2).map { case Seq(prev, current) => (current, Some(prev)) }.toList
28 | urls.foreach(str => app.goTo(routing.matchUrl(Url(str))))
29 | retrying(urlWithRef.toList shouldBe expected)
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/core/.js/src/test/scala/io/udash/testing/TestRouting.scala:
--------------------------------------------------------------------------------
1 | package io.udash.testing
2 |
3 | import io.udash._
4 | import io.udash.routing.RoutingEngine
5 |
6 | trait TestRouting {
7 |
8 | var routing: TestRoutingRegistry = _
9 | var viewFactory: TestViewFactory[ErrorState.type] = _
10 | var vpRegistry: TestViewFactoryRegistry = _
11 | var renderer: TestViewRenderer = _
12 | private[udash] var routingEngine: RoutingEngine[TestState] = _
13 |
14 | protected final def initTestRouting(routing: TestRoutingRegistry = new TestRoutingRegistry,
15 | state2vp: Map[TestState, () => ViewFactory[_ <: TestState]] = Map.empty, default: () => ViewFactory[_ <: TestState] = () => viewFactory
16 | ): Unit = {
17 | this.routing = routing
18 | viewFactory = new TestViewFactory[ErrorState.type]
19 | vpRegistry = new TestViewFactoryRegistry(state2vp, default)
20 | renderer = new TestViewRenderer
21 | }
22 |
23 | protected final def initTestRoutingEngine(routing: TestRoutingRegistry = new TestRoutingRegistry,
24 | state2vp: Map[TestState, () => ViewFactory[_ <: TestState]] = Map.empty
25 | ): Unit = {
26 | initTestRouting(routing, state2vp)
27 | routingEngine = new RoutingEngine[TestState](routing, vpRegistry, renderer)
28 | }
29 | }
--------------------------------------------------------------------------------
/core/.js/src/test/scala/io/udash/testing/TestState.scala:
--------------------------------------------------------------------------------
1 | package io.udash.testing
2 |
3 | import io.udash._
4 |
5 | sealed abstract class TestState(val parentState: Option[ContainerTestState]) extends State {
6 | override type HierarchyRoot = TestState
7 | }
8 | sealed abstract class ContainerTestState(parentState: Option[ContainerTestState]) extends TestState(parentState)
9 | sealed abstract class FinalTestState(parentState: Option[ContainerTestState]) extends TestState(parentState)
10 |
11 | case class RootState(sth: Option[Int]) extends ContainerTestState(None)
12 | case class ClassState(arg: String, arg2: Int) extends FinalTestState(Some(RootState(None)))
13 | case object ObjectState extends ContainerTestState(Some(RootState(None)))
14 | case object ThrowExceptionState extends ContainerTestState(Some(RootState(None)))
15 | case object NextObjectState extends FinalTestState(Some(ObjectState))
16 | case object ErrorState extends FinalTestState(Some(RootState(None)))
17 |
--------------------------------------------------------------------------------
/core/.js/src/test/scala/io/udash/testing/TestUrlChangeProvider.scala:
--------------------------------------------------------------------------------
1 | package io.udash.testing
2 |
3 | import io.udash._
4 | import io.udash.utils.{Registration, SetRegistration}
5 |
6 | import scala.collection.mutable
7 |
8 | class TestUrlChangeProvider(init: Url) extends UrlChangeProvider {
9 | var currUrl: Url = init
10 | var urlsHistory: mutable.ArrayBuffer[Url] = mutable.ArrayBuffer.empty
11 | val changeListeners: mutable.Set[Url => Unit] = mutable.Set.empty
12 |
13 | override def initialize(): Unit = ()
14 |
15 | override def changeFragment(url: Url, changeHistory: Boolean): Unit = {
16 | currUrl = url
17 | if (changeHistory || urlsHistory.isEmpty) urlsHistory.append(url)
18 | else urlsHistory.update(urlsHistory.length - 1, url)
19 | changeListeners.foreach(_(url))
20 | }
21 |
22 | override def changeUrl(url: Url): Unit = changeFragment(url)
23 |
24 | override def currentFragment: Url = currUrl
25 |
26 | override def onFragmentChange(callback: Url => Unit): Registration = {
27 | changeListeners.add(callback)
28 | new SetRegistration(changeListeners, callback)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/core/.js/src/test/scala/io/udash/testing/TestViewFactoryRegistry.scala:
--------------------------------------------------------------------------------
1 | package io.udash.testing
2 |
3 | import io.udash._
4 |
5 | import scala.collection.mutable
6 |
7 | class TestViewFactoryRegistry(vp: Map[TestState, () => ViewFactory[_ <: TestState]],
8 | default: () => ViewFactory[_ <: TestState]) extends ViewFactoryRegistry[TestState] {
9 | var statesHistory: mutable.ArrayBuffer[TestState] = mutable.ArrayBuffer.empty
10 |
11 | override def matchStateToResolver(state: TestState): ViewFactory[_ <: TestState] = {
12 | if (state == ThrowExceptionState) throw new RuntimeException("ThrowExceptionState")
13 | statesHistory.append(state)
14 | vp.get(state).map(_.apply()).getOrElse(default())
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/core/.js/src/test/scala/io/udash/testing/TestViewRenderer.scala:
--------------------------------------------------------------------------------
1 | package io.udash.testing
2 |
3 | import com.avsystem.commons._
4 | import io.udash._
5 | import io.udash.view.ViewRenderer
6 |
7 | class TestViewRenderer extends ViewRenderer(null) {
8 | val views = MArrayBuffer[View]()
9 | var lastSubPathToLeave: List[View] = Nil
10 | var lastPathToAdd: Iterable[View] = Nil
11 |
12 | override def renderView(subPathToLeave: Iterator[View], pathToAdd: Iterable[View]): Unit = {
13 | val subPathList = subPathToLeave.toList
14 | views.clear()
15 | views.appendAll(subPathList)
16 | views.appendAll(pathToAdd)
17 |
18 | lastSubPathToLeave = subPathList
19 | lastPathToAdd = pathToAdd
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/core/.js/src/test/scala/io/udash/testing/UdashCoreFrontendTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package testing
3 |
4 | trait UdashCoreFrontendTest extends UdashCoreTest with UdashFrontendTest
5 | trait AsyncUdashCoreFrontendTest extends AsyncUdashCoreTest with AsyncUdashFrontendTest
--------------------------------------------------------------------------------
/core/src/main/scala/io/udash/properties/HasGenCodecAndModelPropertyCreator.scala:
--------------------------------------------------------------------------------
1 | package io.udash.properties
2 |
3 | import com.avsystem.commons.meta.MacroInstances
4 | import com.avsystem.commons.serialization.GenCodec
5 |
6 | trait GenCodecAndModelPropertyCreator[T] {
7 | def codec: GenCodec[T]
8 | def modelPropertyCreator: ModelPropertyCreator[T]
9 | }
10 |
11 | abstract class HasGenCodecAndModelPropertyCreator[T](implicit
12 | instances: MacroInstances[Unit, GenCodecAndModelPropertyCreator[T]]
13 | ) {
14 | implicit final lazy val modelPropertyCreator: ModelPropertyCreator[T] = instances((), this).modelPropertyCreator
15 | implicit final lazy val codec: GenCodec[T] = instances((), this).codec
16 | }
17 |
--------------------------------------------------------------------------------
/core/src/main/scala/io/udash/properties/HasModelPropertyCreator.scala:
--------------------------------------------------------------------------------
1 | package io.udash.properties
2 |
3 | import com.avsystem.commons.meta.MacroInstances
4 |
5 | abstract class HasModelPropertyCreator[T](implicit instances: MacroInstances[Unit, () => ModelPropertyCreator[T]]) {
6 | implicit final lazy val modelPropertyCreator: ModelPropertyCreator[T] = instances((), this).apply()
7 | }
--------------------------------------------------------------------------------
/core/src/main/scala/io/udash/properties/IsModelPropertyTemplate.scala:
--------------------------------------------------------------------------------
1 | package io.udash.properties
2 |
3 | /**
4 | * Evidence that type `T` can be used to create ModelProperty.
5 | *
6 | * There are two valid model bases:
7 | *
8 | * - trait (not sealed trait) with following restrictions:
9 | * - it cannot contain vars
10 | * - it can contain implemented vals and defs, but they are not considered as subproperties
11 | * - all abstract vals and defs (without parameters) are considered as subproperties
12 | *
13 | * - (case) class with following restrictions:
14 | * - it cannot contain vars
15 | * - it can contain implemented vals and defs, but they are not considered as subproperties
16 | * - it cannot have more than one parameters list in primary constructor
17 | * - all elements of primary constructor are considered as subproperties
18 | *
19 | *
20 | */
21 | class IsModelPropertyTemplate[T]
22 | object IsModelPropertyTemplate {
23 | implicit def checkModelPropertyTemplate[T]: IsModelPropertyTemplate[T] =
24 | macro io.udash.macros.PropertyMacros.checkModelPropertyTemplate[T]
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/core/src/main/scala/io/udash/properties/MutableSetRegistration.scala:
--------------------------------------------------------------------------------
1 | package io.udash.properties
2 |
3 | import com.avsystem.commons._
4 | import io.udash.utils.Registration
5 |
6 | private[udash] class MutableSetRegistration[ElementType](
7 | s: MSet[ElementType], el: ElementType,
8 | statusChangeListener: Opt[() => Unit]
9 | ) extends Registration {
10 | override def cancel(): Unit = {
11 | s -= el
12 | statusChangeListener.foreach(_.apply())
13 | }
14 |
15 | override def restart(): Unit = {
16 | s += el
17 | statusChangeListener.foreach(_.apply())
18 | }
19 |
20 | override def isActive: Boolean = s.contains(el)
21 | }
22 |
--------------------------------------------------------------------------------
/core/src/main/scala/io/udash/properties/PropertyCreatorImplicits.scala:
--------------------------------------------------------------------------------
1 | package io.udash.properties
2 |
3 | trait PropertyCreatorImplicits { this: PropertyCreator.type =>
4 | implicit final def materializeSingle[T]: PropertyCreator[T] = new SinglePropertyCreator[T]
5 | }
--------------------------------------------------------------------------------
/core/src/main/scala/io/udash/properties/seq/Patch.scala:
--------------------------------------------------------------------------------
1 | package io.udash.properties.seq
2 |
3 | import com.avsystem.commons.misc.AbstractCase
4 | import io.udash.properties.single.ReadableProperty
5 |
6 | /**
7 | * Describes changes in SeqProperty structure.
8 | *
9 | * @param idx Index where changes starts.
10 | * @param removed Properties removed from index `idx`.
11 | * @param added Properties added on index `idx`.
12 | * @tparam P Contained properties type.
13 | */
14 | final case class Patch[+P <: ReadableProperty[_]](idx: Int, removed: Seq[P], added: Seq[P]) extends AbstractCase
15 |
--------------------------------------------------------------------------------
/core/src/main/scala/io/udash/properties/single/DirectProperty.scala:
--------------------------------------------------------------------------------
1 | package io.udash.properties.single
2 |
3 | private[properties] final class DirectProperty[A](override protected val parent: ReadableProperty[_])
4 | extends AbstractProperty[A] with CastableProperty[A] {
5 |
6 | private var value: A = _
7 |
8 | override def get: A = value
9 |
10 | override def set(t: A, force: Boolean = false): Unit =
11 | if (force || value != t) {
12 | value = t
13 | valueChanged()
14 | }
15 |
16 | override def setInitValue(t: A): Unit =
17 | value = t
18 |
19 | override def touch(): Unit =
20 | valueChanged()
21 |
22 | override def toString: String = s"DirectProperty($value)"
23 | }
24 |
--------------------------------------------------------------------------------
/core/src/main/scala/io/udash/properties/single/ForwarderProperty.scala:
--------------------------------------------------------------------------------
1 | package io.udash.properties.single
2 |
3 | private[properties] trait ForwarderReadableProperty[A] extends AbstractReadableProperty[A] {
4 | protected def origin: ReadableProperty[_]
5 |
6 | override protected[properties] def parent: ReadableProperty[_] = null
7 | }
8 |
9 | private[properties] trait ForwarderProperty[A] extends ForwarderReadableProperty[A] with AbstractProperty[A] {
10 | protected def origin: Property[_]
11 | }
12 |
--------------------------------------------------------------------------------
/core/src/test/scala/io/udash/properties/HasGenCodecAndModelPropertyCreatorTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash.properties
2 |
3 | import com.avsystem.commons.serialization.json.JsonStringOutput
4 | import io.udash.properties.model.ModelProperty
5 | import io.udash.testing.UdashCoreTest
6 |
7 | class HasGenCodecAndModelPropertyCreatorTest extends UdashCoreTest {
8 | import HasGenCodecAndModelPropertyCreatorTest._
9 |
10 | "HasGenCodecAndModelPropertyCreator class" should {
11 | "provide valid GenCodec" in {
12 | JsonStringOutput.write(Entity(1, "a", Some(Entity(2, "b", None)))) should be(
13 | """{"i":1,"s":"a","e":{"i":2,"s":"b","e":null}}"""
14 | )
15 | }
16 |
17 | "provide valid ModelPropertyCreator" in {
18 | val p = ModelProperty(Entity(1, "a", Some(Entity(2, "b", None))))
19 |
20 | p.subProp(_.i).get should be(1)
21 | p.subProp(_.s).get should be("a")
22 | }
23 | }
24 | }
25 |
26 | object HasGenCodecAndModelPropertyCreatorTest {
27 | case class Entity(i: Int, s: String, e: Option[Entity])
28 | object Entity extends HasGenCodecAndModelPropertyCreator[Entity]
29 | }
30 |
--------------------------------------------------------------------------------
/core/src/test/scala/io/udash/testing/AsyncUdashCoreTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package testing
3 |
4 | trait AsyncUdashCoreTest extends AsyncUdashSharedTest with CoreTestUtils
--------------------------------------------------------------------------------
/core/src/test/scala/io/udash/testing/CoreTestUtils.scala:
--------------------------------------------------------------------------------
1 | package io.udash.testing
2 |
3 | import io.udash.properties.seq.ReadableSeqProperty
4 | import io.udash.properties.single.ReadableProperty
5 | import org.scalatest.Assertion
6 | import org.scalatest.matchers.should.Matchers
7 |
8 | trait CoreTestUtils extends Matchers {
9 | def ensureNoListeners(seqProperty: ReadableSeqProperty[_, _ <: ReadableProperty[_]]): Assertion = {
10 | seqProperty.listenersCount() should be(0)
11 | seqProperty.structureListenersCount() should be(0)
12 | seqProperty.elemProperties.map(_.listenersCount()).sum should be(0)
13 | }
14 |
15 | def valuesOfType[ReturnType](obj: Any): List[ReturnType] = macro io.udash.macros.AllValuesMacro.ofType[ReturnType]
16 | }
17 |
--------------------------------------------------------------------------------
/core/src/test/scala/io/udash/testing/UdashCoreTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash.testing
2 |
3 | trait UdashCoreTest extends UdashSharedTest with CoreTestUtils
--------------------------------------------------------------------------------
/css/.jvm/src/main/scala/io/udash/css/CssStringRenderer.scala:
--------------------------------------------------------------------------------
1 | package io.udash.css
2 |
3 | import scalacss.internal.Renderer
4 |
5 | /** Renders provided styles into `String`. Keeps styles order from provided `Seq`. */
6 | class CssStringRenderer(styles: Seq[CssBase]) {
7 | def render()(implicit renderer: Renderer[String]): String = {
8 | val builder = new StringBuilder
9 |
10 | styles.foreach { style =>
11 | builder.append(style.render)
12 | }
13 |
14 | builder.mkString
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/css/src/main/scala/io/udash/css/CssStyle.scala:
--------------------------------------------------------------------------------
1 | package io.udash.css
2 |
3 | import scalacss.internal.{FontFace, StyleS}
4 |
5 | /** Representation of stylesheet elements. In JS it's always `CssStyleName`. */
6 | sealed trait CssStyle {
7 | def className: String
8 | // Primarily introduced for FontAwesome support, which requires adding two classes, e.g. "fa fa-adjust"
9 | def commonPrefixClass: Option[String] = None
10 | def classNames: Seq[String] = commonPrefixClass.toList :+ className
11 | }
12 | case class CssStyleName(className: String) extends CssStyle
13 | case class CssPrefixedStyleName(prefixClass: String, actualClassSuffix: String) extends CssStyle {
14 | val className = s"$prefixClass-$actualClassSuffix"
15 | override val commonPrefixClass: Option[String] = Some(prefixClass)
16 | }
17 | case class CssStyleNameWithSharedCompanion(companionClass: String, commonPrefix: String, className: String) extends CssStyle {
18 | override val commonPrefixClass: Option[String] = Some(commonPrefix)
19 | override def classNames: Seq[String] = Seq(companionClass, className)
20 | }
21 | case class CssStyleImpl(className: String, impl: StyleS) extends CssStyle
22 | case class CssKeyframes(className: String, steps: Seq[(Double, StyleS)]) extends CssStyle
23 | case class CssFontFace(className: String, font: FontFace[Option[String]]) extends CssStyle
24 |
--------------------------------------------------------------------------------
/css/src/main/scala/io/udash/css/CssText.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package css
3 |
4 | import scalatags.Text.all._
5 | import scalatags.text.Builder
6 |
7 | trait CssText {
8 | implicit def style2TextMod(s: CssStyle): Modifier = new CssText.TextStyleModifier(s)
9 | }
10 |
11 | object CssText extends CssText {
12 | private final class TextStyleModifier(styles: CssStyle*) extends Modifier {
13 | override def applyTo(t: Builder): Unit =
14 | styles.foreach { s => t.appendAttr(cls.name, Builder.GenericAttrValueSource(s.classNames.mkString(" "))) }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/css/src/test/scala/io/udash/css/CssTextTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package css
3 |
4 | import io.udash.testing.UdashSharedTest
5 |
6 | class CssTextTest extends UdashSharedTest {
7 |
8 | import CssText._
9 | import StylesheetExample._
10 | import scalatags.Text.all._
11 |
12 | "CssText" should {
13 | "provide tools for rendering styles with Scalatags text nodes" in {
14 | div(test1, test2, indent(2))("Test").render shouldBe
15 | "Test
"
16 | }
17 |
18 | "separate class names by space" in {
19 | val fontAwesomeStyle = CssStyleNameWithSharedCompanion("far", "fa", "fa-grin")
20 | i(fontAwesomeStyle).render shouldBe ""
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/css/src/test/scala/io/udash/css/SecondStylesheetExample.scala:
--------------------------------------------------------------------------------
1 | package io.udash.css
2 |
3 |
4 | object SecondStylesheetExample extends CssBase {
5 | import dsl._
6 |
7 | def utils(x: Int): CssStyle = mixin(
8 | margin(x px, auto),
9 | textAlign.left,
10 | cursor.pointer
11 | )
12 |
13 | val test: CssStyle = style(
14 | utils(12),
15 |
16 | &.hover(
17 | cursor.zoomIn
18 | ),
19 |
20 | media.not.handheld.landscape.maxWidth(640 px)(
21 | width(400 px)
22 | )
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/guide/backend/src/main/resources/demo_translations_en.properties:
--------------------------------------------------------------------------------
1 | auth.login.buttonLabel=Sign in
2 | auth.login.retriesLeft={} retries left
3 | auth.login.retriesLeftOne=1 retry left
4 | auth.loginLabel=Username
5 | auth.passwordLabel=Password
6 | auth.register.buttonLabel=Sign up
7 |
8 | server.exception.example=Something went wrong!
--------------------------------------------------------------------------------
/guide/backend/src/main/resources/demo_translations_pl.properties:
--------------------------------------------------------------------------------
1 | auth.login.buttonLabel=Zaloguj
2 | auth.login.retriesLeft=Zosta\u0142o kilka pr\u00F3b
3 | auth.login.retriesLeftOne=Zosta\u0142a ostatnia pr\u00F3ba
4 | auth.loginLabel=Nazwa u\u017Cytkownika
5 | auth.passwordLabel=Has\u0142o
6 | auth.register.buttonLabel=Zarejestruj
7 |
8 | server.exception.example=Przet\u0142umaczony b\u0142\u0105d!
--------------------------------------------------------------------------------
/guide/backend/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | logs/udash-guide-${bySecond}.log
17 | true
18 |
19 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/guide/backend/src/main/resources/reference.conf:
--------------------------------------------------------------------------------
1 | ui {
2 | server {
3 | port = 8080
4 | homepageResourceBase = "guide/homepage/.js/target/UdashStatics/WebContent/homepage/"
5 | guideResourceBase = "guide/guide/.js/target/UdashStatics/WebContent/guide/"
6 | }
7 | }
--------------------------------------------------------------------------------
/guide/backend/src/main/scala/io/udash/web/Implicits.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web
2 |
3 | import java.util.concurrent.Executors
4 |
5 | import scala.concurrent.ExecutionContext
6 |
7 | object Implicits {
8 | implicit val backendExecutionContext: ExecutionContext = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(32))
9 | }
10 |
--------------------------------------------------------------------------------
/guide/backend/src/main/scala/io/udash/web/Launcher.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web
2 |
3 | import com.typesafe.config.ConfigFactory
4 | import com.typesafe.scalalogging.LazyLogging
5 | import io.udash.web.server.ApplicationServer
6 | import monix.execution.Scheduler
7 |
8 | object Launcher extends LazyLogging {
9 | def main(args: Array[String]): Unit = {
10 | val startTime = System.nanoTime
11 |
12 | createApplicationServer().start()
13 |
14 | val duration: Long = (System.nanoTime - startTime) / 1000000000
15 | logger.info(s"Udash Homepage & Dev's Guide started in ${duration}s.")
16 | }
17 |
18 |
19 | private[udash] def createApplicationServer(): ApplicationServer = {
20 | implicit val scheduler: Scheduler = Scheduler.computation()
21 | val serverConfig = ConfigFactory.load().getConfig("ui.server")
22 | new ApplicationServer(
23 | port = serverConfig.getInt("port"),
24 | homepageResourceBase = serverConfig.getString("homepageResourceBase"),
25 | guideResourceBase = serverConfig.getString("guideResourceBase")
26 | )
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/guide/backend/src/main/scala/io/udash/web/guide/demos/DemosServer.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos
2 |
3 | import io.udash.i18n.RemoteTranslationRPC
4 | import io.udash.rpc._
5 | import io.udash.web.guide.demos.activity.{CallLogger, CallServer, CallServerRPC}
6 | import io.udash.web.guide.demos.i18n.TranslationServer
7 | import io.udash.web.guide.demos.rpc._
8 |
9 | class DemosServer(callLogger: CallLogger)(implicit clientId: ClientId) extends DemosServerRPC {
10 | override def pingDemo: PingServerRPC = new PingServer
11 | override def clientIdDemo: ClientIdServerRPC = new ClientIdServer
12 | override def notificationsDemo: NotificationsServerRPC = new NotificationsServer
13 | override def gencodecsDemo: GenCodecServerRPC = new GenCodecServer
14 | override def translations: RemoteTranslationRPC = new TranslationServer
15 | override def exceptions: ExceptionsRPC = new ExceptionsServer
16 |
17 | override def call: CallServerRPC = new CallServer(callLogger)
18 | }
19 |
--------------------------------------------------------------------------------
/guide/backend/src/main/scala/io/udash/web/guide/demos/activity/CallLogger.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos.activity
2 |
3 | import scala.collection.mutable.ListBuffer
4 |
5 | class CallLogger {
6 | private val _calls = ListBuffer.empty[Call]
7 |
8 | def append(call: Call): Unit = _calls.synchronized {
9 | _calls += call
10 | if (_calls.size > 20) _calls.remove(0, _calls.size-20)
11 | }
12 |
13 | def calls: List[Call] = _calls.synchronized {
14 | _calls.toList
15 | }
16 | }
--------------------------------------------------------------------------------
/guide/backend/src/main/scala/io/udash/web/guide/demos/activity/CallServer.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos.activity
2 |
3 | import scala.concurrent.Future
4 |
5 | class CallServer(callLogger: CallLogger) extends CallServerRPC {
6 | override def calls: Future[Seq[Call]] = Future.successful(callLogger.calls)
7 | }
8 |
--------------------------------------------------------------------------------
/guide/backend/src/main/scala/io/udash/web/guide/demos/i18n/TranslationServer.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos.i18n
2 |
3 | import io.udash.i18n.{Lang, ResourceBundlesTranslationTemplatesProvider, TranslationRPCEndpoint}
4 | import io.udash.web.Implicits.*
5 |
6 | import java.util as ju
7 |
8 | class TranslationServer extends TranslationRPCEndpoint(
9 | new ResourceBundlesTranslationTemplatesProvider(
10 | TranslationServer.langs
11 | .map(lang =>
12 | Lang(lang) -> TranslationServer.bundlesNames.map(name => ju.ResourceBundle.getBundle(name, new ju.Locale.Builder().setLanguage(lang).build()))
13 | ).toMap
14 | )
15 | )
16 |
17 | object TranslationServer {
18 | val langs = Seq("en", "pl")
19 | val bundlesNames = Seq("demo_translations")
20 | }
--------------------------------------------------------------------------------
/guide/backend/src/main/scala/io/udash/web/guide/demos/rpc/ClientIdServer.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos.rpc
2 |
3 | import io.udash.rpc._
4 |
5 | import scala.concurrent.Future
6 |
7 | class ClientIdServer(implicit cid: ClientId) extends ClientIdServerRPC {
8 | override def clientId(): Future[String] = {
9 | Future.successful(cid.toString)
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/guide/backend/src/main/scala/io/udash/web/guide/demos/rpc/ExceptionsServer.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos.rpc
2 |
3 | import io.udash.web.guide.GuideExceptions
4 | import io.udash.web.guide.demos.i18n.Translations
5 |
6 | import scala.concurrent.Future
7 |
8 | class ExceptionsServer extends ExceptionsRPC {
9 | override def example(): Future[Unit] =
10 | Future.failed(GuideExceptions.ExampleException("Exception from server"))
11 |
12 | override def exampleWithTranslatableError(): Future[Unit] =
13 | Future.failed(GuideExceptions.TranslatableExampleException(Translations.exceptions.example))
14 |
15 | override def unknownError(): Future[Unit] =
16 | Future.failed(new RuntimeException("RuntimeException from server"))
17 | }
18 |
--------------------------------------------------------------------------------
/guide/backend/src/main/scala/io/udash/web/guide/demos/rpc/PingServer.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos.rpc
2 |
3 | import io.udash.rpc._
4 | import io.udash.web.guide.rpc.ClientRPC
5 |
6 | import scala.concurrent.Future
7 |
8 | class PingServer(implicit clientId: ClientId) extends PingServerRPC {
9 | import io.udash.web.Implicits._
10 |
11 | override def ping(id: Int): Unit = {
12 | ClientRPC(clientId).demos().pingDemo().pong(id)
13 | }
14 |
15 | override def fPing(id: Int): Future[Int] = {
16 | Future.successful(id)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/guide/backend/src/main/scala/io/udash/web/guide/rest/ExposedRestInterfaces.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.rest
2 |
3 | import io.udash.web.guide.demos.rest._
4 |
5 | import scala.concurrent.Future
6 |
7 | class ExposedRestInterfaces extends MainServerREST {
8 | override def simple(): SimpleServerREST = new SimpleServerREST {
9 | override def string(): Future[String] = Future.successful("OK")
10 | override def cls(): Future[RestExampleClass] = Future.successful(RestExampleClass(42, "Udash", InnerClass(321.123, "REST Support")))
11 | override def int(): Future[Int] = Future.successful(123)
12 | }
13 | override def echo(): EchoServerREST = new EchoServerREST {
14 | override def withUrlPart(arg: String): Future[String] = Future.successful(s"URL:$arg")
15 | override def withQuery(arg: String): Future[String] = Future.successful(s"Query:$arg")
16 | override def withHeader(arg: String): Future[String] = Future.successful(s"Header:$arg")
17 | override def withBody(arg: String): Future[String] = Future.successful(s"Body:$arg")
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/guide/backend/src/main/scala/io/udash/web/guide/rpc/ClientRPC.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.rpc
2 |
3 | import io.udash.web.guide.MainClientRPC
4 | import io.udash.rpc._
5 |
6 | import scala.concurrent.ExecutionContext
7 |
8 | object ClientRPC {
9 | def apply(target: ClientRPCTarget)(implicit ec: ExecutionContext): MainClientRPC = {
10 | new DefaultClientRPC[MainClientRPC](target).get
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/guide/backend/src/main/scala/io/udash/web/guide/rpc/ExposedRpcInterfaces.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.rpc
2 |
3 | import io.udash.rpc._
4 | import io.udash.web.guide.MainServerRPC
5 | import io.udash.web.guide.demos.activity.CallLogger
6 | import io.udash.web.guide.demos.{DemosServer, DemosServerRPC}
7 | import io.udash.web.guide.markdown.{MarkdownPageRPC, MarkdownPagesEndpoint}
8 |
9 | class ExposedRpcInterfaces(callLogger: CallLogger, guideResourceBase: String)(implicit clientId: ClientId) extends MainServerRPC {
10 | import io.udash.web.Implicits._
11 |
12 | override val demos: DemosServerRPC = new DemosServer(callLogger)
13 | override val pages: MarkdownPageRPC = new MarkdownPagesEndpoint(guideResourceBase)
14 | }
--------------------------------------------------------------------------------
/guide/commons/.js/src/main/assets/pdf/origami_crane_printok.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/commons/.js/src/main/assets/pdf/origami_crane_printok.pdf
--------------------------------------------------------------------------------
/guide/commons/.js/src/main/assets/svg/avsystem.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/guide/commons/.js/src/main/assets/svg/based.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/guide/commons/.js/src/main/assets/svg/github.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/guide/commons/.js/src/main/assets/svg/gitter.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/guide/commons/.js/src/main/assets/svg/icon_submenu.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/guide/commons/.js/src/main/assets/svg/shared_code.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
23 |
--------------------------------------------------------------------------------
/guide/commons/.js/src/main/assets/svg/stack.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/guide/commons/.js/src/main/assets/svg/todomvc.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/guide/commons/.js/src/main/scala/io/udash/web/commons/components/ForceBootstrap.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package web.commons.components
3 |
4 | import org.scalajs.dom.html.Div
5 | import scalatags.JsDom
6 |
7 | object ForceBootstrap {
8 | import scalatags.JsDom.all._
9 |
10 | def apply(modifiers: Modifier*): JsDom.TypedTag[Div] =
11 | div(cls := "bootstrap")( //force Bootstrap styles
12 | modifiers:_*
13 | )
14 | }
15 |
--------------------------------------------------------------------------------
/guide/commons/.js/src/main/scala/io/udash/web/commons/components/HeaderNav.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.commons.components
2 |
3 | import io.udash.web.commons.styles.components.HeaderNavStyles
4 | import org.scalajs.dom.Element
5 |
6 | trait HeaderNav {
7 | import io.udash.css.CssView._
8 | import scalatags.JsDom.all._
9 | import scalatags.JsDom.tags2.nav
10 |
11 | val navStyles: HeaderNavStyles
12 |
13 | case class NavItem(url: String, title: String)
14 |
15 | def navigation(items: NavItem*): Element =
16 | nav(navStyles.headerNav)(
17 | ul(navStyles.headerLinkList)(
18 | items.map(item =>
19 | li(navStyles.headerLinkWrapper)(
20 | a(href := item.url, navStyles.headerLink)(item.title)
21 | )
22 | )
23 | )
24 | ).render
25 | }
26 |
--------------------------------------------------------------------------------
/guide/commons/.js/src/main/scala/io/udash/web/commons/config/ExternalUrls.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.commons.config
2 |
3 | object ExternalUrls {
4 | val udashGitter = "https://gitter.im/UdashFramework/udash-core?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"
5 | val udashGithub = "https://github.com/UdashFramework/"
6 | val udashG8Build = "https://raw.githubusercontent.com/UdashFramework/udash.g8/master/src/main/g8/build.sbt"
7 | val udashG8Plugins = "https://raw.githubusercontent.com/UdashFramework/udash.g8/master/src/main/g8/project/plugins.sbt"
8 | val udashG8Index = "https://raw.githubusercontent.com/UdashFramework/udash.g8/master/src/main/g8/frontend/src/main/assets/index.html"
9 | val udashDemos = "https://github.com/UdashFramework/udash-demos"
10 | val stackoverflow = "http://stackoverflow.com/questions/tagged/udash"
11 | val avsystem = "http://www.avsystem.com/"
12 | val homepage = "http://udash.io/"
13 | val guide = "http://guide.udash.io/"
14 | val scalajs = "https://www.scala-js.org/"
15 |
16 | val releases = "https://github.com/UdashFramework/udash-core/releases"
17 | val license = "http://guide.udash.io/license"
18 | }
--------------------------------------------------------------------------------
/guide/commons/.js/src/main/scala/io/udash/web/commons/views/Component.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.commons.views
2 |
3 | import io.udash.css.CssView
4 | import org.scalajs.dom
5 | import org.scalajs.dom._
6 |
7 | import scalatags.generic.Modifier
8 |
9 | trait Component extends Modifier[dom.Element] with CssView {
10 | def getTemplate: Modifier[dom.Element]
11 |
12 | def apply(): Modifier[dom.Element] = getTemplate
13 |
14 | override def applyTo(t: Element): Unit =
15 | getTemplate.applyTo(t)
16 | }
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/assets.less:
--------------------------------------------------------------------------------
1 | @import (inline) "prism.css";
2 | .bootstrap {
3 | @import (less) "lib/bootstrap/css/bootstrap.css";
4 | }
5 | @import (inline) "lib/font-awesome/css/all.min.css";
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/fonts/roboto/Roboto-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/fonts/roboto/Roboto-Black.ttf
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/fonts/roboto/Roboto-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/fonts/roboto/Roboto-BlackItalic.ttf
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/fonts/roboto/Roboto-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/fonts/roboto/Roboto-Bold.ttf
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/fonts/roboto/Roboto-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/fonts/roboto/Roboto-BoldItalic.ttf
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/fonts/roboto/Roboto-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/fonts/roboto/Roboto-Italic.ttf
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/fonts/roboto/Roboto-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/fonts/roboto/Roboto-Light.ttf
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/fonts/roboto/Roboto-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/fonts/roboto/Roboto-LightItalic.ttf
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/fonts/roboto/Roboto-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/fonts/roboto/Roboto-Medium.ttf
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/fonts/roboto/Roboto-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/fonts/roboto/Roboto-MediumItalic.ttf
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/fonts/roboto/Roboto-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/fonts/roboto/Roboto-Regular.ttf
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/fonts/roboto/Roboto-Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/fonts/roboto/Roboto-Thin.ttf
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/fonts/roboto/Roboto-ThinItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/fonts/roboto/Roboto-ThinItalic.ttf
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/images/ext/bootstrap/carousel.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/images/ext/bootstrap/carousel.jpg
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/images/favicon.ico
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/images/intro_bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/images/intro_bg.jpg
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/images/intro_bird.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/images/intro_bird.png
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/images/quick/generator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/images/quick/generator.png
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/images/share/share_facebook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/images/share/share_facebook.png
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/images/share/share_google.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/images/share/share_google.png
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/images/share/share_twitter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/images/share/share_twitter.png
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/images/udash_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/images/udash_logo.png
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/images/udash_logo_l.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/images/udash_logo_l.png
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/images/udash_logo_m.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/images/udash_logo_m.png
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/images/views/bootstrapping/modules_basic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/images/views/bootstrapping/modules_basic.png
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/images/views/bootstrapping/modules_extended.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/images/views/bootstrapping/modules_extended.png
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/images/views/bootstrapping/states.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/images/views/bootstrapping/states.png
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/images/views/frontend/mvp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/images/views/frontend/mvp.png
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/images/views/frontend/property.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/images/views/frontend/property.png
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/assets/images/views/frontend/propertyhierarchy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/guide/.js/src/main/assets/images/views/frontend/propertyhierarchy.png
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/components/BootstrapUtils.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.components
2 |
3 | import io.udash.bootstrap.utils.BootstrapStyles
4 | import io.udash.bootstrap.utils.BootstrapStyles.Color
5 | import io.udash.css.CssStyleName
6 |
7 | object BootstrapUtils {
8 |
9 | /**
10 | * Wells component from bootstrap3 is absent in bootstrap4.
11 | * These well-like styles make elements look like the good old bootstrap3 well.
12 | *
13 | * Source: https://getbootstrap.com/docs/3.3/components/#wells
14 | */
15 | def wellStyles: Seq[CssStyleName] = Seq(
16 | BootstrapStyles.Card.card,
17 | BootstrapStyles.Card.body,
18 | BootstrapStyles.Background.color(Color.Light),
19 | )
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/components/Header.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.components
2 |
3 | import io.udash.web.commons.components.{HeaderButtons, HeaderNav}
4 | import io.udash.web.commons.config.ExternalUrls
5 | import io.udash.web.commons.styles.GlobalStyles
6 | import io.udash.web.commons.styles.components.{HeaderButtonsStyles, HeaderNavStyles}
7 | import io.udash.web.commons.views.Image
8 | import io.udash.web.guide.styles.partials.HeaderStyles
9 |
10 | import scalatags.JsDom.all._
11 |
12 | object Header extends HeaderButtons with HeaderNav {
13 | import io.udash.css.CssView._
14 | private lazy val template = header(HeaderStyles.header)(
15 | div(GlobalStyles.body, GlobalStyles.clearfix)(
16 | div(HeaderStyles.headerLeft)(
17 | a(HeaderStyles.headerLogo, href := ExternalUrls.homepage)(
18 | Image("udash_logo_m.png", "Udash Framework", GlobalStyles.block)
19 | )/*,
20 | navigation(Seq(
21 | NavItem(ExternalUrls.guide, "Documentation"),
22 | NavItem(ExternalUrls.releases, "Changelog")
23 | ))*/
24 | ),
25 | buttons
26 | )
27 | )
28 |
29 | def getTemplate: Modifier = template
30 |
31 | override val buttonStyles: HeaderButtonsStyles = HeaderStyles
32 | override val navStyles: HeaderNavStyles = HeaderStyles
33 | }
34 |
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/demos/DemosClient.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos
2 |
3 | import io.udash.web.guide.demos.rpc.{NotificationsClient, NotificationsClientRPC, PingClient, PingClientRPC}
4 |
5 | object DemosClient extends DemosClientRPC {
6 | override def pingDemo(): PingClientRPC = PingClient
7 | override def notificationsDemo(): NotificationsClientRPC = NotificationsClient
8 | }
9 |
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/demos/rpc/NotificationsClient.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos.rpc
2 |
3 | import io.udash.web.guide.Context
4 |
5 | import scala.concurrent.Future
6 |
7 | object NotificationsClient extends NotificationsClientRPC {
8 |
9 | import Context._
10 |
11 | private val listeners = scala.collection.mutable.ArrayBuffer[String => Any]()
12 |
13 | def registerListener(listener: String => Any): Future[Unit] = {
14 | listeners += listener
15 | if (listeners.size == 1) serverRpc.demos.notificationsDemo.register()
16 | else Future.unit
17 | }
18 |
19 | def unregisterListener(listener: String => Any): Future[Unit] = {
20 | listeners -= listener
21 | if (listeners.isEmpty) serverRpc.demos.notificationsDemo.unregister()
22 | else Future.unit
23 | }
24 |
25 | override def notify(msg: String): Unit = {
26 | listeners.foreach(_ (msg))
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/demos/rpc/PingClient.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos.rpc
2 |
3 | object PingClient extends PingClientRPC {
4 | private val pongListeners = scala.collection.mutable.ArrayBuffer[Int => Any]()
5 |
6 | override def pong(id: Int): Unit = {
7 | pongListeners.foreach(l => l(id))
8 | }
9 |
10 | def registerPongListener(listener: Int => Any) = pongListeners += listener
11 | def unregisterPongListener(listener: Int => Any) = pongListeners -= listener
12 | }
13 |
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/rpc/RPCService.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.rpc
2 |
3 | import io.udash.web.guide.MainClientRPC
4 | import io.udash.web.guide.demos.{DemosClient, DemosClientRPC}
5 |
6 | class RPCService extends MainClientRPC {
7 | override def demos(): DemosClientRPC = DemosClient
8 | }
9 |
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/views/ContentView.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.views
2 |
3 | import io.udash.*
4 | import io.udash.web.commons.styles.GlobalStyles
5 | import io.udash.web.guide.ContentState
6 | import io.udash.web.guide.components.GuideMenu
7 | import io.udash.web.guide.styles.partials.GuideStyles
8 | import org.scalajs.dom.Element
9 | import scalatags.JsDom.tags2.*
10 |
11 | object ContentViewFactory extends StaticViewFactory[ContentState.type](() => new ContentView)
12 |
13 | class ContentView extends ViewContainer {
14 | import io.udash.css.CssView._
15 |
16 | import scalatags.JsDom.all._
17 |
18 | override protected val child: Element = main(GuideStyles.contentWrapper).render
19 |
20 | private val content = main(GuideStyles.main)(
21 | div(GlobalStyles.body)(
22 | div(GuideStyles.menuWrapper)(
23 | GuideMenu().getTemplate
24 | ),
25 | child
26 | )
27 | )
28 |
29 | override def getTemplate: Modifier = content
30 | }
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/views/FaqView.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.views
2 |
3 | import io.udash._
4 | import io.udash.web.guide.FaqState
5 |
6 | object FaqViewFactory extends StaticViewFactory[FaqState.type](() => new FaqView)
7 |
8 | class FaqView extends View {
9 | import scalatags.JsDom.all._
10 |
11 | private val content = div(
12 | h2("FAQ"),
13 | p("TODO")
14 | )
15 |
16 | override def getTemplate: Modifier = content
17 | }
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/views/References.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.views
2 |
3 | object References {
4 | val UdashjQueryWrapperRepo = "https://github.com/UdashFramework/scala-js-jquery"
5 | val UdashGuideRepo = "https://github.com/UdashFramework/udash-core/tree/master/guide"
6 | val UdashG8Repo = "https://github.com/UdashFramework/udash.g8"
7 | val UdashFilesDemoRepo = "https://github.com/UdashFramework/udash-demos/tree/master/file-upload"
8 | val AvScalaCommonsGitHub = "https://github.com/AVSystem/scala-commons"
9 | val BootstrapHomepage = "http://getbootstrap.com/"
10 | val JettyHomepage = "http://www.eclipse.org/jetty/"
11 | val MvpPattern = "https://martinfowler.com/eaaDev/uiArchs.html#Model-view-presentermvp"
12 | val ScalaCssHomepage = "https://github.com/japgolly/scalacss"
13 | val ScalaJsHomepage = "http://www.scala-js.org/"
14 | val ScalaHomepage = "http://www.scala-lang.org/"
15 | val ScalatagsHomepage = "https://github.com/lihaoyi/scalatags"
16 | val UpickleHomepage = "https://github.com/lihaoyi/upickle-pprint"
17 | }
18 |
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/views/RootView.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.views
2 |
3 | import io.udash.*
4 | import io.udash.web.commons.components.Footer
5 | import io.udash.web.guide.RootState
6 | import io.udash.web.guide.components.Header
7 | import org.scalajs.dom.Element
8 |
9 | object RootViewFactory extends StaticViewFactory[RootState.type](() => new RootView)
10 |
11 | class RootView extends ViewContainer {
12 | import scalatags.JsDom.all._
13 |
14 | override protected val child: Element = div().render
15 |
16 | private val content = div(
17 | Header.getTemplate,
18 | child,
19 | Footer.getTemplate()
20 | )
21 |
22 | override def getTemplate: Modifier = content
23 | }
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/views/Versions.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.views
2 |
3 | object Versions {
4 | val udashVersion = "0.8.0"
5 | val udashJQueryVersion = "3.0.2"
6 | }
7 |
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/views/ViewContainer.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.views
2 |
3 | import io.udash._
4 | import io.udash.wrappers.jquery.EasingFunction
5 | import org.scalajs.dom._
6 |
7 | import scala.scalajs.js
8 |
9 | abstract class ViewContainer extends ContainerView {
10 | protected val child: Element
11 |
12 | override def renderChild(view: Option[View]): Unit = {
13 | import io.udash.wrappers.jquery.jQ
14 | val jqChild = jQ(child)
15 |
16 | jqChild
17 | .animate(Map[String, Any]("opacity" -> 0), 150, EasingFunction.swing,
18 | (_: Element) => {
19 | view match {
20 | case Some(view) =>
21 | jqChild.children().remove()
22 | view.getTemplate.applyTo(jqChild.toArray.head)
23 | jqChild.animate(Map[String, Any]("opacity" -> 1), 200, EasingFunction.swing,
24 | _ => js.Dynamic.global.Prism.highlightAll()
25 | )
26 | case None =>
27 | jqChild.html(null)
28 | .animate(Map[String, Any]("opacity" -> 1), 200)
29 | }
30 | }
31 | )
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/views/bootstrapping/BootstrappingView.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.views.bootstrapping
2 |
3 | import io.udash.*
4 | import io.udash.web.guide.views.ViewContainer
5 | import io.udash.web.guide.{Context, *}
6 | import org.scalajs.dom.Element
7 | import scalatags.JsDom
8 |
9 | case object BootstrappingViewFactory extends StaticViewFactory[BootstrappingState.type](() => new BootstrappingView)
10 |
11 | class BootstrappingView extends ViewContainer {
12 | import Context._
13 | import JsDom.all._
14 |
15 | override protected val child: Element = div().render
16 |
17 | override def getTemplate: Modifier = div(
18 | h1("Application bootstrapping"),
19 | p("In this part of the guide you will read about bootstrapping an Udash application from scratch."),
20 | p(
21 | i("This is an advanced topic, if you want to start development as soon as possible, generate the ",
22 | a(href := IntroState.url)("sample application"), ".")
23 | ),
24 | child
25 | )
26 | }
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/views/ext/demo/RpcLoggingDemo.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.views.ext.demo
2 |
3 | import com.avsystem.commons._
4 | import io.udash._
5 | import io.udash.bootstrap.utils.BootstrapStyles
6 | import io.udash.bootstrap.utils.BootstrapStyles.Color
7 | import io.udash.web.guide.demos.activity.Call
8 | import io.udash.web.guide.styles.partials.GuideStyles
9 | import org.scalajs.dom
10 | import org.scalajs.dom._
11 | import scalatags.JsDom.all._
12 |
13 | object RpcLoggingDemo {
14 | import io.udash.css.CssView._
15 | def apply(model: ReadableSeqProperty[Call], loadCalls: () => Any): dom.Element =
16 | span(GuideStyles.frame, GuideStyles.useBootstrap)(
17 | button(
18 | id := "call-logging-demo", BootstrapStyles.Button.btn, BootstrapStyles.Button.color(Color.Primary),
19 | onclick :+= ((_: MouseEvent) => loadCalls().thenReturn(true))
20 | )("Load call list"),
21 | produce(model)(seq => ul(seq.map(call => li(call.toString))).render)
22 | ).render
23 | }
24 |
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/views/ext/demo/bootstrap/BadgesDemo.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.views.ext.demo.bootstrap
2 |
3 | import io.udash.bootstrap.button.UdashButtonOptions
4 | import io.udash.css.CssView
5 | import io.udash.web.guide.demos.AutoDemo
6 | import io.udash.web.guide.styles.partials.GuideStyles
7 | import scalatags.JsDom.all._
8 |
9 | object BadgesDemo extends AutoDemo with CssView {
10 |
11 | private val (rendered, source) = {
12 | import io.udash._
13 | import io.udash.bootstrap.badge.UdashBadge
14 | import io.udash.bootstrap.button.UdashButton
15 | import io.udash.bootstrap.utils.BootstrapStyles._
16 | import org.scalajs.dom.window
17 | import scalatags.JsDom.all._
18 |
19 | val counter = Property(0)
20 | window.setInterval(() => counter.set(counter.get + 1), 3000)
21 |
22 | div(
23 | UdashButton(
24 | options = UdashButtonOptions(
25 | color = Color.Primary.opt,
26 | size = Size.Large.opt
27 | )
28 | )(_ => Seq[Modifier](
29 | "Button ",
30 | UdashBadge()(nested => nested(bind(counter))
31 | ).render
32 | ))
33 | ).render
34 | }.withSourceCode
35 |
36 | override protected def demoWithSource(): (Modifier, String) =
37 | (rendered.setup(_.applyTags(GuideStyles.frame)), source)
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/views/ext/demo/bootstrap/ButtonToolbarDemo.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.views.ext.demo.bootstrap
2 |
3 | import io.udash.css.CssView
4 | import io.udash.web.guide.demos.AutoDemo
5 | import io.udash.web.guide.styles.partials.GuideStyles
6 | import scalatags.JsDom.all._
7 |
8 | object ButtonToolbarDemo extends AutoDemo with CssView {
9 |
10 | private val (rendered, source) = {
11 | import io.udash._
12 | import io.udash.bootstrap.button._
13 | import io.udash.bootstrap.utils.BootstrapStyles._
14 | import scalatags.JsDom.all._
15 |
16 | val groups = SeqProperty[Seq[Int]](Seq[Seq[Int]](1 to 4, 5 to 7, 8 to 8))
17 |
18 | div(
19 | UdashButtonToolbar.reactive(groups)((p, nested) => {
20 | val group = UdashButtonGroup.reactive(
21 | p.transformToSeq(identity),
22 | size = Some(Size.Large).toProperty[Option[Size]]
23 | ) {
24 | case (element, nested) =>
25 | val btn = UdashButton()(_ => nested(bind(element)))
26 | nested(btn)
27 | btn.render
28 | }
29 | nested(group)
30 | group.render
31 | })
32 | ).render
33 | }.withSourceCode
34 |
35 | override protected def demoWithSource(): (Modifier, String) =
36 | (rendered.setup(_.applyTags(GuideStyles.frame)), source)
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/views/ext/demo/bootstrap/InlineFormDemo.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.views.ext.demo.bootstrap
2 |
3 | import io.udash.css.CssView
4 | import io.udash.web.guide.demos.AutoDemo
5 | import io.udash.web.guide.styles.partials.GuideStyles
6 | import scalatags.JsDom.all._
7 |
8 | object InlineFormDemo extends AutoDemo with CssView {
9 |
10 | private val (rendered, source) = {
11 | import io.udash._
12 | import io.udash.bootstrap.form.{UdashForm, UdashInputGroup}
13 | import scalatags.JsDom.all._
14 |
15 | val search = Property.blank[String]
16 | val something = Property.blank[String]
17 |
18 | div(
19 | UdashForm(inline = true)(factory => Seq(
20 | UdashInputGroup()(
21 | UdashInputGroup.prependText("Search: "),
22 | UdashInputGroup.input(
23 | factory.input.textInput(search)().render
24 | )
25 | ).render,
26 | UdashInputGroup()(
27 | UdashInputGroup.prependText("Something: "),
28 | UdashInputGroup.input(
29 | factory.input.textInput(something)().render
30 | )
31 | ).render,
32 | ))
33 | ).render
34 | }.withSourceCode
35 |
36 | override protected def demoWithSource(): (Modifier, String) =
37 | (rendered.setup(_.applyTags(GuideStyles.frame)), source)
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/views/ext/demo/bootstrap/JumbotronDemo.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.views.ext.demo.bootstrap
2 |
3 | import io.udash.bootstrap.button.UdashButtonOptions
4 | import io.udash.css.CssView
5 | import io.udash.web.guide.demos.AutoDemo
6 | import io.udash.web.guide.styles.partials.GuideStyles
7 | import scalatags.JsDom.all._
8 |
9 | object JumbotronDemo extends AutoDemo with CssView {
10 |
11 | private val (rendered, source) = {
12 | import io.udash.bootstrap.button.UdashButton
13 | import io.udash.bootstrap.jumbotron.UdashJumbotron
14 | import io.udash.bootstrap.utils.BootstrapStyles._
15 | import scalatags.JsDom.all._
16 |
17 | UdashJumbotron()(_ => Seq[Modifier](
18 | h1("Jumbo poem!"),
19 | p("One component to rule them all, one component to find them, " +
20 | "one component to bring them all and in the darkness bind them."
21 | ),
22 | UdashButton(
23 | options = UdashButtonOptions(
24 | color = Color.Info.opt,
25 | size = Size.Large.opt
26 | )
27 | )(_ => "Click")
28 | ))
29 | }.withSourceCode
30 |
31 | override protected def demoWithSource(): (Modifier, String) =
32 | (div(GuideStyles.frame)(rendered), source)
33 | }
34 |
35 |
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/views/ext/demo/bootstrap/ResponsiveEmbedDemo.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.views.ext.demo.bootstrap
2 |
3 | import io.udash.web.guide.demos.AutoDemo
4 | import io.udash.web.guide.styles.partials.GuideStyles
5 | import scalatags.JsDom.all._
6 |
7 | object ResponsiveEmbedDemo extends AutoDemo {
8 |
9 | private val (rendered, source) = {
10 | import io.udash.bootstrap.utils.BootstrapStyles._
11 | import io.udash.css.CssView._
12 | import scalatags.JsDom.all._
13 |
14 | div(
15 | div(
16 | EmbedResponsive.responsive,
17 | EmbedResponsive.embed16by9,
18 | Spacing.margin(size = SpacingSize.Small)
19 | )(
20 | iframe(
21 | EmbedResponsive.item,
22 | src := "https://www.youtube.com/embed/zpOULjyy-n8?rel=0"
23 | )
24 | ),
25 | div(
26 | EmbedResponsive.responsive,
27 | EmbedResponsive.embed4by3,
28 | Spacing.margin(size = SpacingSize.Small)
29 | )(
30 | iframe(
31 | EmbedResponsive.item,
32 | src := "https://www.youtube.com/embed/zpOULjyy-n8?rel=0"
33 | )
34 | )
35 | ).render
36 | }.withSourceCode
37 |
38 | override protected def demoWithSource(): (Modifier, String) = {
39 | import io.udash.css.CssView._
40 | (rendered.setup(_.applyTags(GuideStyles.frame)), source)
41 | }
42 | }
43 |
44 |
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/views/ext/demo/bootstrap/StaticButtonsGroupDemo.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.views.ext.demo.bootstrap
2 |
3 | import io.udash.css.CssView
4 | import io.udash.web.guide.demos.AutoDemo
5 | import io.udash.web.guide.styles.partials.GuideStyles
6 | import scalatags.JsDom.all._
7 |
8 | object StaticButtonsGroupDemo extends AutoDemo with CssView {
9 |
10 | private val (rendered, source) = {
11 | import io.udash._
12 | import io.udash.bootstrap._
13 | import BootstrapStyles.Color
14 | import io.udash.bootstrap.button._
15 | import scalatags.JsDom.all._
16 |
17 | div(
18 | UdashButtonGroup(vertical = true.toProperty)(
19 | UdashButton()("Button 1").render,
20 | UdashButton()("Button 2").render,
21 | UdashButton()("Button 3").render
22 | )
23 | ).render
24 | }.withSourceCode
25 |
26 | override protected def demoWithSource(): (Modifier, String) =
27 | (rendered.setup(_.applyTags(GuideStyles.frame)), source)
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/views/frontend/FrontendView.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.views.frontend
2 |
3 | import io.udash.*
4 | import io.udash.web.guide.FrontendState
5 | import io.udash.web.guide.views.ViewContainer
6 | import org.scalajs.dom.Element
7 | import scalatags.JsDom
8 |
9 | case object FrontendViewFactory extends StaticViewFactory[FrontendState.type](() => new FrontendView)
10 |
11 |
12 | class FrontendView extends ViewContainer {
13 | import JsDom.all._
14 |
15 | override protected val child: Element = div().render
16 |
17 | override def getTemplate: Modifier = div(
18 | h1("Frontend"),
19 | p(
20 | "In this part of the guide you will read about creating a frontend application with Udash. Let's make your ",
21 | "frontend type-safe, elegant and maintainable. "
22 | ),
23 | child
24 | )
25 | }
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/views/frontend/demos/BindAttributeDemo.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.views.frontend.demos
2 |
3 | import io.udash.css.CssView
4 | import io.udash.web.guide.demos.AutoDemo
5 | import io.udash.web.guide.styles.partials.GuideStyles
6 | import scalatags.JsDom.all._
7 |
8 | object BindAttributeDemo extends AutoDemo with CssView {
9 |
10 | private val (rendered, source) = {
11 | import io.udash._
12 | import org.scalajs.dom.window
13 | import scalatags.JsDom.all._
14 |
15 | val visible = Property(true)
16 | window.setInterval(() => visible.set(!visible.get), 1000)
17 |
18 | p(
19 | span("Visible: ", bind(visible), " -> "),
20 | span((style := "display: none;").attrIfNot(visible))("Show/hide")
21 | )
22 | }.withSourceCode
23 |
24 | override protected def demoWithSource(): (Modifier, String) =
25 | (
26 | div(
27 | id := "bind-attr-demo",
28 | GuideStyles.frame
29 | )(rendered),
30 | source
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/views/frontend/demos/BindDemo.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.views.frontend.demos
2 |
3 | import io.udash.css.CssView
4 | import io.udash.web.guide.demos.AutoDemo
5 | import io.udash.web.guide.styles.partials.GuideStyles
6 | import scalatags.JsDom.all._
7 |
8 | object BindDemo extends AutoDemo with CssView {
9 |
10 | private val (rendered, source) = {
11 | import io.udash._
12 | import org.scalajs.dom.window
13 | import scalatags.JsDom.all._
14 |
15 | val names = LazyList.continually(LazyList("John", "Amy", "Bryan", "Diana")).flatten.iterator
16 |
17 | val name = Property(names.next())
18 | window.setInterval(() => name.set(names.next()), 500)
19 |
20 | p("Name: ", bind(name))
21 | }.withSourceCode
22 |
23 | override protected def demoWithSource(): (Modifier, String) =
24 | (
25 | div(
26 | id := "bind-demo",
27 | GuideStyles.frame
28 | )(rendered),
29 | source
30 | )
31 | }
32 |
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/views/frontend/demos/ShowIfDemo.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.views.frontend.demos
2 |
3 | import io.udash.css.CssView
4 | import io.udash.web.guide.demos.AutoDemo
5 | import io.udash.web.guide.styles.partials.GuideStyles
6 | import scalatags.JsDom.all._
7 |
8 | object ShowIfDemo extends AutoDemo with CssView {
9 |
10 | private val (rendered, source) = {
11 | import io.udash._
12 | import org.scalajs.dom.window
13 | import scalatags.JsDom.all._
14 |
15 | val visible = Property(true)
16 | window.setInterval(() => visible.set(!visible.get), 1000)
17 |
18 | p(
19 | span("Visible: ", bind(visible), " -> "),
20 | showIf(visible)(span("Show/hide").render)
21 | )
22 | }.withSourceCode
23 |
24 | override protected def demoWithSource(): (Modifier, String) =
25 | (div(id := "show-if-demo", GuideStyles.frame)(rendered), source)
26 | }
27 |
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/views/frontend/demos/TextAreaDemo.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.views.frontend.demos
2 |
3 | import io.udash.web.guide.demos.AutoDemo
4 | import io.udash.web.guide.styles.partials.GuideStyles
5 | import scalatags.JsDom.all._
6 |
7 | object TextAreaDemo extends AutoDemo {
8 |
9 | private val (rendered, source) = {
10 | import io.udash._
11 | import io.udash.bootstrap.utils.BootstrapStyles._
12 | import io.udash.css.CssView._
13 | import scalatags.JsDom.all._
14 |
15 | val text = Property("")
16 |
17 | form(containerFluid)(
18 | div(Grid.row)(
19 | div(Grid.col(4, ResponsiveBreakpoint.Medium))(
20 | TextArea(text)(Form.control)
21 | ),
22 | div(Grid.col(4, ResponsiveBreakpoint.Medium))(
23 | TextArea(text)(Form.control)
24 | ),
25 | div(Grid.col(4, ResponsiveBreakpoint.Medium))(
26 | TextArea(text)(Form.control)
27 | )
28 | )
29 | )
30 | }.withSourceCode
31 |
32 | override protected def demoWithSource(): (Modifier, String) = {
33 | import io.udash.css.CssView._
34 | (div(id := "text-area-demo", GuideStyles.frame, GuideStyles.useBootstrap)(rendered), source)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/guide/guide/.js/src/main/scala/io/udash/web/guide/views/rpc/RpcView.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.views.rpc
2 |
3 | import io.udash.*
4 | import io.udash.web.guide.*
5 | import io.udash.web.guide.views.ViewContainer
6 | import org.scalajs.dom.Element
7 | import scalatags.JsDom
8 |
9 | case object RpcViewFactory extends StaticViewFactory[RpcState.type](() => new RpcView)
10 |
11 | class RpcView extends ViewContainer {
12 | import JsDom.all._
13 |
14 | override protected val child: Element = div().render
15 |
16 | override def getTemplate: Modifier = div(
17 | h1("RPC in Udash"),
18 | p("In this part of the guide you can read about client-server communication in a Udash application."),
19 | child
20 | )
21 | }
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/assets.less:
--------------------------------------------------------------------------------
1 | @import (inline) "prism.css";
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/fonts/roboto/Roboto-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/fonts/roboto/Roboto-Black.ttf
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/fonts/roboto/Roboto-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/fonts/roboto/Roboto-BlackItalic.ttf
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/fonts/roboto/Roboto-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/fonts/roboto/Roboto-Bold.ttf
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/fonts/roboto/Roboto-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/fonts/roboto/Roboto-BoldItalic.ttf
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/fonts/roboto/Roboto-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/fonts/roboto/Roboto-Italic.ttf
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/fonts/roboto/Roboto-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/fonts/roboto/Roboto-Light.ttf
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/fonts/roboto/Roboto-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/fonts/roboto/Roboto-LightItalic.ttf
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/fonts/roboto/Roboto-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/fonts/roboto/Roboto-Medium.ttf
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/fonts/roboto/Roboto-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/fonts/roboto/Roboto-MediumItalic.ttf
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/fonts/roboto/Roboto-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/fonts/roboto/Roboto-Regular.ttf
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/fonts/roboto/Roboto-Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/fonts/roboto/Roboto-Thin.ttf
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/fonts/roboto/Roboto-ThinItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/fonts/roboto/Roboto-ThinItalic.ttf
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/images/favicon.ico
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/images/features_compiled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/images/features_compiled.png
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/images/features_shared.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/images/features_shared.png
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/images/features_typesafe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/images/features_typesafe.png
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/images/intro_bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/images/intro_bg.jpg
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/images/intro_bird.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/images/intro_bird.png
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/images/laptop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/images/laptop.png
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/images/share/share_facebook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/images/share/share_facebook.png
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/images/share/share_google.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/images/share/share_google.png
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/images/share/share_twitter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/images/share/share_twitter.png
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/images/udash_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/images/udash_logo.png
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/images/udash_logo_l.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/images/udash_logo_l.png
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/assets/images/udash_logo_m.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UdashFramework/udash-core/bc1969122eaf0e057803c052a0be3861f123d975/guide/homepage/.js/src/main/assets/images/udash_logo_m.png
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/scala/io/udash/web/homepage/RoutingRegistryDef.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.homepage
2 |
3 | import io.udash._
4 |
5 | class RoutingRegistryDef extends RoutingRegistry[RoutingState] {
6 | def matchUrl(url: Url): RoutingState =
7 | url2State.applyOrElse("/" + url.value.stripPrefix("/").stripSuffix("/"), (_: String) => ErrorState)
8 |
9 | def matchState(state: RoutingState): Url =
10 | Url(state2Url.apply(state))
11 |
12 | private val (url2State, state2Url) = bidirectional {
13 | case "/" => HelloState
14 | case "/demo" / "select" => SelectState
15 | }
16 | }
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/scala/io/udash/web/homepage/StatesToViewFactoryDef.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.homepage
2 |
3 | import io.udash._
4 | import io.udash.web.homepage.views._
5 |
6 | final class StatesToViewFactoryDef extends ViewFactoryRegistry[RoutingState] {
7 |
8 | import Context.applicationInstance
9 |
10 | def matchStateToResolver(state: RoutingState): ViewFactory[_ <: RoutingState] = state match {
11 | case RootState => RootViewFactory
12 | case _: IndexState => new IndexViewFactory
13 | case _ => ErrorViewFactory
14 | }
15 | }
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/scala/io/udash/web/homepage/components/Buttons.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.homepage.components
2 |
3 | import io.udash.web.homepage.styles.partials.ButtonsStyle
4 |
5 | import scalatags.JsDom.all._
6 |
7 | /**
8 | * Created by malchik on 2016-04-04.
9 | */
10 | object Buttons {
11 | import io.udash.css.CssView._
12 | def whiteBorderButton(link: String, label: String, xs: Modifier*): Modifier =
13 | a(href := link, target := "_blank", ButtonsStyle.btnDefault, xs: Modifier)(
14 | div(ButtonsStyle.btnDefaultInner)(label)
15 | )
16 |
17 | def blackBorderButton(link: String, label: String, xs: Modifier*): Modifier =
18 | a(href := link, target := "_blank", ButtonsStyle.btnDefault, ButtonsStyle.btnDefaultBlack, xs: Modifier)(
19 | div(ButtonsStyle.btnDefaultInner, ButtonsStyle.btnDefaultInnerBlack)(label)
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/scala/io/udash/web/homepage/components/demo/CodeDemo.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.homepage.components.demo
2 |
3 | import com.avsystem.commons.SharedExtensions._
4 | import io.udash._
5 | import scalatags.JsDom.all._
6 |
7 | trait CodeDemo {
8 | def rendered: Modifier
9 | def source: String
10 | }
11 |
12 | object HelloDemo extends CodeDemo {
13 | val (rendered: Modifier, source) = {
14 | val name = Property.blank[String]
15 | div(
16 | TextInput(name)(),
17 | p("Hello, ", bind(name), "!"),
18 | )
19 | }.withSourceCode
20 | }
21 |
22 | object SelectDemo extends CodeDemo {
23 | val (rendered: Modifier, source) = {
24 | import com.avsystem.commons.misc.AutoNamedEnum
25 |
26 | sealed trait Fruit extends AutoNamedEnum
27 | case object Apple extends Fruit
28 | case object Banana extends Fruit
29 | case object Orange extends Fruit
30 |
31 | val fruits = Seq(Apple, Banana, Orange)
32 | val favoriteFruits = SeqProperty[Fruit](Banana)
33 |
34 | div(
35 | div(Select(favoriteFruits, fruits.toSeqProperty)(_.name)),
36 | div(produce(favoriteFruits)(_.mkString(",").render)),
37 | )
38 | }.withSourceCode
39 | }
40 |
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/scala/io/udash/web/homepage/init.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.homepage
2 |
3 | import io.udash._
4 | import io.udash.logging.CrossLogging
5 | import io.udash.routing.WindowUrlPathChangeProvider
6 |
7 | import scala.scalajs.js.annotation.JSExport
8 |
9 | object Context {
10 | private val routingRegistry = new RoutingRegistryDef
11 | private val viewFactoriesRegistry = new StatesToViewFactoryDef
12 |
13 | implicit val applicationInstance: Application[RoutingState] =
14 | new Application[RoutingState](routingRegistry, viewFactoriesRegistry, new WindowUrlPathChangeProvider)
15 | }
16 |
17 | object Init extends CrossLogging {
18 | import Context._
19 |
20 | @JSExport
21 | def main(args: Array[String]): Unit =
22 | applicationInstance.run("#application")
23 | }
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/scala/io/udash/web/homepage/states.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.homepage
2 |
3 | import com.avsystem.commons.misc.AbstractSealedEnumCompanion
4 | import io.udash._
5 | import io.udash.web.homepage.components.demo.{CodeDemo, HelloDemo, SelectDemo}
6 |
7 | sealed abstract class RoutingState(val parentState: Option[ContainerRoutingState]) extends State {
8 | override type HierarchyRoot = RoutingState
9 | def url(implicit application: Application[RoutingState]): String = s"${application.matchState(this).value}"
10 | }
11 | sealed abstract class ContainerRoutingState(parentState: Option[ContainerRoutingState]) extends RoutingState(parentState)
12 |
13 | case object RootState extends ContainerRoutingState(None)
14 |
15 | case object ErrorState extends RoutingState(Some(RootState))
16 |
17 | sealed abstract class IndexState(val name: String, val codeDemo: CodeDemo) extends RoutingState(Some(RootState))
18 | case object HelloState extends IndexState("Hello, World!", HelloDemo)
19 | case object SelectState extends IndexState("Select", SelectDemo)
20 | object IndexState extends AbstractSealedEnumCompanion[IndexState] {
21 | override val values: Seq[IndexState] = caseObjects
22 | }
--------------------------------------------------------------------------------
/guide/homepage/.js/src/main/scala/io/udash/web/homepage/views/RootView.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.homepage.views
2 |
3 | import com.avsystem.commons._
4 | import io.udash._
5 | import io.udash.css.CssView
6 | import io.udash.web.commons.components.Footer
7 | import io.udash.web.commons.styles.GlobalStyles
8 | import io.udash.web.homepage.RootState
9 | import io.udash.web.homepage.components.Header
10 | import io.udash.web.homepage.styles.partials.HomepageStyles
11 | import scalatags.JsDom.tags2._
12 |
13 | import scala.scalajs.js
14 |
15 | object RootViewFactory extends StaticViewFactory[RootState.type](() => new RootView)
16 |
17 | class RootView extends ContainerView with CssView {
18 | import scalatags.JsDom.all._
19 |
20 | private val content = div(
21 | Header.getTemplate,
22 | main(GlobalStyles.main)(
23 | childViewContainer
24 | ),
25 | Footer.getTemplate(HomepageStyles.body.opt)
26 | )
27 |
28 | override def getTemplate: Modifier = content
29 |
30 | override def renderChild(view: Option[View]): Unit = {
31 | super.renderChild(view)
32 | js.Dynamic.global.svg4everybody()
33 | }
34 | }
--------------------------------------------------------------------------------
/guide/packager/src/main/resources/application.conf:
--------------------------------------------------------------------------------
1 | ui {
2 | server {
3 | port = 8080
4 | homepageResourceBase = "UdashStatics/WebContent/homepage/"
5 | guideResourceBase = "UdashStatics/WebContent/guide/"
6 | }
7 | }
--------------------------------------------------------------------------------
/guide/selenium/src/test/resources/application.conf:
--------------------------------------------------------------------------------
1 | ui.server.port = 8089
--------------------------------------------------------------------------------
/guide/selenium/src/test/scala/io/udash/web/guide/demos/rpc/RpcBackendTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos.rpc
2 |
3 | import io.udash.web.SeleniumTest
4 | import org.scalatest.BeforeAndAfterEach
5 |
6 | class RpcBackendTest extends SeleniumTest with BeforeAndAfterEach {
7 | override protected final val url = "/rpc/client-server"
8 |
9 | "RpcBackend view" should {
10 | "receive ClientId in demo" in {
11 | val callDemo = findElementById("client-id-demo")
12 | var response = findElementById("client-id-demo-response")
13 |
14 | callDemo.isEnabled should be(true)
15 | response.getText.equalsIgnoreCase("???") should be(true)
16 |
17 | callDemo.click()
18 |
19 | eventually {
20 | response = findElementById("client-id-demo-response")
21 | response.getText.startsWith("ClientId") should be(true)
22 | callDemo.isEnabled should be(false)
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/guide/selenium/src/test/scala/io/udash/web/guide/demos/rpc/RpcIntroTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos.rpc
2 |
3 | import io.udash.web.SeleniumTest
4 | import org.openqa.selenium.WebElement
5 |
6 | class RpcIntroTest extends SeleniumTest {
7 | override protected final val url = "/rpc"
8 |
9 | "RpcIntro view" should {
10 | "receive response in call demo" in {
11 | val callDemo = findElementById("ping-pong-call-demo")
12 | buttonTest(callDemo)
13 | }
14 |
15 | "receive response in push demo" in {
16 | val pushDemo = findElementById("ping-pong-push-demo")
17 | buttonTest(pushDemo)
18 | }
19 | }
20 |
21 | def buttonTest(callDemo: WebElement): Unit = {
22 | for (i <- 1 to 3) {
23 | callDemo.click()
24 | eventually {
25 | callDemo.isEnabled should be(true)
26 | callDemo.getText should be(s"Ping($i)")
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/guide/selenium/src/test/scala/io/udash/web/guide/demos/rpc/RpcSerializationTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos.rpc
2 |
3 | import io.udash.web.SeleniumTest
4 |
5 | class RpcSerializationTest extends SeleniumTest {
6 | override protected final val url = "/rpc/serialization"
7 |
8 | "RpcSerialization view" should {
9 | "receive msg from backend" in {
10 | val callDemo = findElementById("gencodec-demo")
11 |
12 | callDemo.isEnabled should be(true)
13 | callDemo.click()
14 |
15 | eventually {
16 | findElementById("gencodec-demo-int").getText shouldNot be(empty)
17 | findElementById("gencodec-demo-double").getText shouldNot be(empty)
18 | findElementById("gencodec-demo-string").getText shouldNot be(empty)
19 | findElementById("gencodec-demo-seq").getText shouldNot be(empty)
20 | findElementById("gencodec-demo-map").getText shouldNot be(empty)
21 | findElementById("gencodec-demo-caseClass").getText shouldNot be(empty)
22 | findElementById("gencodec-demo-cls-int").getText shouldNot be(empty)
23 | findElementById("gencodec-demo-cls-string").getText shouldNot be(empty)
24 | findElementById("gencodec-demo-cls-var").getText shouldNot be(empty)
25 | findElementById("gencodec-demo-sealedTrait").getText shouldNot be(empty)
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/commons/styles/attributes/Attributes.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.commons.styles.attributes
2 |
3 | object Attributes {
4 | /**
5 | * States
6 | */
7 | val Hidden = "hidden"
8 | val Active = "active"
9 | val Disabled = "disabled"
10 | val Enabled = "enabled"
11 | val Checked = "checked"
12 | val Show = "show"
13 | val Expanded = "expanded"
14 | val State = "state"
15 | val Pinned = "pinned"
16 |
17 | /**
18 | * Generate attribute data-attr
19 | * @param attr name of attribute
20 | * @return attribute data-attr
21 | */
22 | def data(attr: String) = s"data-$attr"
23 | }
24 |
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/commons/styles/components/CodeBlockStyles.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.commons.styles.components
2 |
3 | import io.udash.css.{CssBase, CssStyle}
4 |
5 | trait CodeBlockStyles extends CssBase {
6 | val codeWrapper: CssStyle
7 | val codeBlock: CssStyle
8 | }
9 |
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/commons/styles/utils/MediaQueries.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.commons.styles.utils
2 |
3 | import io.udash.css.{CssBase, CssStyle}
4 | import scalacss.internal.DslBase.ToStyle
5 |
6 | import scala.language.postfixOps
7 |
8 | object MediaQueries extends CssBase {
9 | import dsl._
10 |
11 | def desktop(properties: ToStyle*): CssStyle = mixin(
12 | media.screen.minWidth(StyleConstants.MediaQueriesBounds.TabletLandscapeMax + 1 px) (
13 | properties:_*
14 | )
15 | )
16 |
17 | def tabletLandscape(properties: ToStyle*): CssStyle = mixin(
18 | media.screen.minWidth(1 px).maxWidth(StyleConstants.MediaQueriesBounds.TabletLandscapeMax px) (
19 | properties:_*
20 | )
21 | )
22 |
23 | def tabletPortrait(properties: ToStyle*): CssStyle = mixin(
24 | media.screen.minWidth(1 px).maxWidth(StyleConstants.MediaQueriesBounds.TabletMax px) (
25 | properties:_*
26 | )
27 | )
28 |
29 | def phone(properties: ToStyle*): CssStyle = mixin(
30 | media.screen.minWidth(1 px).maxWidth(StyleConstants.MediaQueriesBounds.PhoneMax px) (
31 | properties:_*
32 | )
33 | )
34 | }
35 |
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/commons/styles/utils/StyleConstants.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.commons.styles.utils
2 |
3 | import io.udash.css.CssBase
4 |
5 | object StyleConstants extends CssBase {
6 | import dsl._
7 |
8 | /**
9 | * SIZES
10 | */
11 | object Sizes {
12 | val BodyWidth = 1075
13 | val BodyPaddingPx = 30
14 | val MinSiteHeight = 550
15 | val LandingPageHeaderHeight = 150
16 | val HeaderHeight = 80
17 | val GuideHeaderHeightMobile = HeaderHeight * .7
18 | val HeaderHeightPin = 80
19 | val FooterHeight = 120
20 | val MenuWidth = 320
21 | }
22 |
23 | /**
24 | * COLORS
25 | */
26 | object Colors {
27 | val Red = c"#e30613"
28 | val RedLight = c"#ff2727"
29 | val RedDark = c"#a6031b"
30 | val Grey = c"#898989"
31 | val GreyExtra = c"#ebebeb"
32 | val GreySemi = c"#cfcfd6"
33 | val GreySuperDark = c"#1c1c1e"
34 | val Yellow = c"#ffd600"
35 | }
36 |
37 | /**
38 | * MEDIA QUERIES
39 | */
40 | object MediaQueriesBounds {
41 | val TabletLandscapeMax = 1074
42 | val TabletMax = 767
43 | val PhoneMax = 480
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/guide/GuideExceptions.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide
2 |
3 | import com.avsystem.commons.SharedExtensions._
4 | import com.avsystem.commons.serialization.GenCodec
5 | import io.udash.i18n.TranslationKey0
6 | import io.udash.rpc.serialization.{DefaultExceptionCodecRegistry, ExceptionCodecRegistry}
7 |
8 | object GuideExceptions {
9 | case class ExampleException(msg: String) extends Exception(msg)
10 | case class TranslatableExampleException(trKey: TranslationKey0) extends Exception
11 |
12 | val registry: ExceptionCodecRegistry = (new DefaultExceptionCodecRegistry).setup { registry =>
13 | registry.register(GenCodec.materialize[ExampleException])
14 | registry.register(GenCodec.materialize[TranslatableExampleException])
15 | registry
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/guide/MainClientRPC.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide
2 |
3 | import io.udash.rpc._
4 | import io.udash.web.guide.demos.DemosClientRPC
5 |
6 | trait MainClientRPC {
7 | def demos(): DemosClientRPC
8 | }
9 |
10 | object MainClientRPC extends DefaultClientRpcCompanion[MainClientRPC]
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/guide/MainServerRPC.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide
2 |
3 | import io.udash.rpc._
4 | import io.udash.web.guide.demos.DemosServerRPC
5 | import io.udash.web.guide.markdown.MarkdownPageRPC
6 |
7 | trait MainServerRPC {
8 | def demos: DemosServerRPC
9 | def pages: MarkdownPageRPC
10 | }
11 |
12 | object MainServerRPC extends DefaultServerRpcCompanion[MainServerRPC]
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/guide/demos/DemosClientRPC.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos
2 |
3 | import io.udash.rpc._
4 | import io.udash.web.guide.demos.rpc.{NotificationsClientRPC, PingClientRPC}
5 |
6 | trait DemosClientRPC {
7 | def pingDemo(): PingClientRPC
8 | def notificationsDemo(): NotificationsClientRPC
9 | }
10 |
11 | object DemosClientRPC extends DefaultClientRpcCompanion[DemosClientRPC]
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/guide/demos/DemosServerRPC.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos
2 |
3 | import io.udash.rpc._
4 | import io.udash.web.guide.demos.activity.CallServerRPC
5 | import io.udash.web.guide.demos.rpc._
6 |
7 | trait DemosServerRPC {
8 | import io.udash.i18n._
9 |
10 | def pingDemo: PingServerRPC
11 | def clientIdDemo: ClientIdServerRPC
12 | def notificationsDemo: NotificationsServerRPC
13 | def gencodecsDemo: GenCodecServerRPC
14 | def translations: RemoteTranslationRPC
15 | def exceptions: ExceptionsRPC
16 |
17 | def call: CallServerRPC
18 | }
19 |
20 | object DemosServerRPC extends DefaultServerRpcCompanion[DemosServerRPC]
21 |
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/guide/demos/activity/Call.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos.activity
2 |
3 | import com.avsystem.commons.serialization.HasGenCodec
4 |
5 | case class Call(rpcName: String, method: String, args: Seq[String]) {
6 | override def toString: String = s"$rpcName.$method args: ${args.mkString("[", ", ", "]")}"
7 | }
8 | object Call extends HasGenCodec[Call]
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/guide/demos/activity/CallServerRPC.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos.activity
2 |
3 | import io.udash.rpc._
4 |
5 | import scala.concurrent.Future
6 |
7 | trait CallServerRPC {
8 | def calls: Future[Seq[Call]]
9 | }
10 |
11 | object CallServerRPC extends DefaultServerRpcCompanion[CallServerRPC]
12 |
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/guide/demos/i18n/Translations.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos.i18n
2 |
3 | import io.udash.i18n.TranslationKey
4 |
5 |
6 | object Translations {
7 | import TranslationKey._
8 |
9 | object auth {
10 | val loginLabel = key("auth.loginLabel")
11 | val passwordLabel = key("auth.passwordLabel")
12 |
13 | object login {
14 | val buttonLabel = key("auth.login.buttonLabel")
15 | val retriesLeft = key1[Int]("auth.login.retriesLeft")
16 | val retriesLeftOne = key("auth.login.retriesLeftOne")
17 | }
18 |
19 | object register {
20 | val buttonLabel = key("auth.register.buttonLabel")
21 | }
22 | }
23 |
24 | object exceptions {
25 | val example = key("server.exception.example")
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/guide/demos/rest/MainServerREST.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos.rest
2 |
3 | import io.udash.rest._
4 |
5 | import scala.concurrent.Future
6 |
7 | trait MainServerREST {
8 | def simple(): SimpleServerREST
9 | def echo(): EchoServerREST
10 | }
11 | object MainServerREST extends DefaultRestApiCompanion[MainServerREST]
12 |
13 | trait SimpleServerREST {
14 | @GET def string(): Future[String]
15 | @GET def int(): Future[Int]
16 | @GET def cls(): Future[RestExampleClass]
17 | }
18 | object SimpleServerREST extends DefaultRestApiCompanion[SimpleServerREST]
19 |
20 | trait EchoServerREST {
21 | def withQuery(@Query("param") arg: String): Future[String]
22 | def withHeader(@Header("X-test") arg: String): Future[String]
23 | def withUrlPart(@Path arg: String): Future[String]
24 | def withBody(@Body arg: String): Future[String]
25 | }
26 | object EchoServerREST extends DefaultRestApiCompanion[EchoServerREST]
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/guide/demos/rest/RestExampleClass.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos.rest
2 |
3 | import io.udash.rest.RestDataCompanion
4 |
5 | case class RestExampleClass(i: Int, s: String, inner: InnerClass)
6 | object RestExampleClass extends RestDataCompanion[RestExampleClass]
7 |
8 | case class InnerClass(d: Double, s: String)
9 | object InnerClass extends RestDataCompanion[InnerClass]
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/guide/demos/rpc/ClientIdServerRPC.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos.rpc
2 |
3 | import io.udash.rpc._
4 | import io.udash.rpc.utils.Logged
5 |
6 | import scala.concurrent.Future
7 |
8 | trait ClientIdServerRPC {
9 | @Logged
10 | def clientId(): Future[String]
11 | }
12 |
13 | object ClientIdServerRPC extends DefaultServerRpcCompanion[ClientIdServerRPC]
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/guide/demos/rpc/ExceptionsRPC.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos.rpc
2 |
3 | import io.udash.rpc._
4 |
5 | import scala.concurrent.Future
6 |
7 | trait ExceptionsRPC {
8 | def example(): Future[Unit]
9 | def exampleWithTranslatableError(): Future[Unit]
10 | def unknownError(): Future[Unit]
11 | }
12 |
13 | object ExceptionsRPC extends DefaultServerRpcCompanion[ExceptionsRPC]
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/guide/demos/rpc/NotificationsClientRPC.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos.rpc
2 |
3 | import io.udash.rpc._
4 |
5 | trait NotificationsClientRPC {
6 | def notify(msg: String): Unit
7 | }
8 |
9 | object NotificationsClientRPC extends DefaultClientRpcCompanion[NotificationsClientRPC]
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/guide/demos/rpc/NotificationsServerRPC.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos.rpc
2 |
3 | import io.udash.rpc._
4 | import io.udash.rpc.utils.Logged
5 |
6 | import scala.concurrent.Future
7 |
8 | trait NotificationsServerRPC {
9 | @Logged
10 | def register(): Future[Unit]
11 | @Logged
12 | def unregister(): Future[Unit]
13 | }
14 |
15 | object NotificationsServerRPC extends DefaultServerRpcCompanion[NotificationsServerRPC]
16 |
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/guide/demos/rpc/PingClientRPC.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos.rpc
2 |
3 | import io.udash.rpc._
4 |
5 | trait PingClientRPC {
6 | def pong(id: Int): Unit
7 | }
8 |
9 | object PingClientRPC extends DefaultClientRpcCompanion[PingClientRPC]
10 |
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/guide/demos/rpc/PingServerRPC.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.demos.rpc
2 |
3 | import io.udash.rpc._
4 | import io.udash.rpc.utils.Logged
5 |
6 | import scala.concurrent.Future
7 |
8 | trait PingServerRPC {
9 | def ping(id: Int): Unit
10 |
11 | @Logged
12 | def fPing(id: Int): Future[Int]
13 | }
14 |
15 | object PingServerRPC extends DefaultServerRpcCompanion[PingServerRPC]
16 |
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/guide/markdown/MarkdownPage.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.markdown
2 |
3 | import com.avsystem.commons.misc.{AbstractValueEnum, AbstractValueEnumCompanion, EnumCtx}
4 |
5 | final class MarkdownPage(val file: String)(implicit val ctx: EnumCtx) extends AbstractValueEnum
6 | object MarkdownPage extends AbstractValueEnumCompanion[MarkdownPage] {
7 | final val Intro: Value = new MarkdownPage("assets/pages/intro.md")
8 | final val Rest: Value = new MarkdownPage("assets/pages/rest.md")
9 | final val License: Value = new MarkdownPage("assets/pages/license.md")
10 | }
11 |
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/guide/markdown/MarkdownPageRPC.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.markdown
2 |
3 | import io.udash.rpc.DefaultServerRpcCompanion
4 |
5 | import scala.concurrent.Future
6 |
7 | trait MarkdownPageRPC {
8 | def loadContent(page: MarkdownPage): Future[String]
9 | }
10 |
11 | object MarkdownPageRPC extends DefaultServerRpcCompanion[MarkdownPageRPC]
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/guide/styles/MarkdownStyles.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.styles
2 |
3 | import io.udash.css.{CssBase, CssStyle}
4 |
5 | import scala.language.postfixOps
6 |
7 | object MarkdownStyles extends CssBase {
8 | import dsl._
9 |
10 | val markdownPage: CssStyle = style(
11 | unsafeChild("li") (
12 | position.relative,
13 | paddingLeft(2 rem),
14 | margin(.5 rem, `0`, .5 rem, 4.5 rem),
15 |
16 | &.before(
17 | position.absolute,
18 | left(`0`),
19 | top(`0`),
20 | content.string("•"),
21 | )
22 | ),
23 |
24 | unsafeChild("iframe")(
25 | marginTop(2.5 rem),
26 |
27 | &.firstChild (
28 | marginTop(`0`)
29 | )
30 | ),
31 |
32 | unsafeChild("pre")(
33 | marginTop(2.5 rem),
34 |
35 | &.firstChild (
36 | marginTop(`0`)
37 | )
38 | ),
39 |
40 | unsafeChild("code") (
41 | backgroundColor(c"#f5f2f0"),
42 | fontFamily :=! "Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace"
43 | ),
44 | )
45 | }
46 |
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/guide/styles/demo/ExampleKeyframes.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.styles.demo
2 |
3 | import io.udash.css.{CssBase, CssStyle}
4 |
5 | object ExampleKeyframes extends CssBase {
6 | import dsl._
7 |
8 | val colorPulse: CssStyle = keyframes(
9 | 0d -> keyframe(
10 | color(c"#000000"),
11 | backgroundColor(c"#FFFFFF")
12 | ),
13 |
14 | 50d -> keyframe(
15 | color(c"#FFFFFF"),
16 | backgroundColor(c"#D9534F")
17 | ),
18 |
19 | 100d -> keyframe(
20 | color(c"#000000"),
21 | backgroundColor(c"#FFFFFF")
22 | )
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/guide/styles/demo/ExampleMixins.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.styles.demo
2 |
3 | import io.udash.css.{CssBase, CssStyle}
4 | import scalacss.internal.AV
5 |
6 | import scala.concurrent.duration.FiniteDuration
7 |
8 | object ExampleMixins extends CssBase {
9 | import dsl._
10 |
11 | def animation(keyframes: CssStyle, duration: FiniteDuration,
12 | iterationCount: AV = animationIterationCount.infinite,
13 | easing: AV = animationTimingFunction.easeInOut): CssStyle = mixin(
14 | animationName(keyframes),
15 | iterationCount,
16 | animationDuration(duration),
17 | easing
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/guide/styles/utils/GuideStyleUtils.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.styles.utils
2 |
3 | import io.udash.css.{CssBase, CssStyle}
4 | import io.udash.web.commons.styles.utils.StyleConstants
5 | import scalacss.internal.Macros.Color
6 | import scalacss.internal.{AV, Attr, Length}
7 |
8 | import scala.concurrent.duration.{DurationInt, FiniteDuration}
9 | import scala.language.postfixOps
10 |
11 | object GuideStyleUtils extends CssBase {
12 | import dsl._
13 |
14 | val relativeMiddle: CssStyle = mixin(
15 | top(50 %%),
16 | transform := "translateY(-50%)",
17 | position.relative
18 | )
19 |
20 | def transition(property: Attr = all, duration: FiniteDuration = 250 milliseconds): CssStyle = mixin(
21 | transitionProperty := property.toString(),
22 | transitionDuration(duration),
23 | transitionTimingFunction.easeInOut
24 | )
25 |
26 | def border(bColor: Color = StyleConstants.Colors.GreyExtra, bWidth: Length[Double] = 1.0 px, bStyle: AV = borderStyle.solid): CssStyle = mixin(
27 | borderWidth(bWidth),
28 | bStyle,
29 | borderColor(bColor)
30 | )
31 | }
32 |
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/guide/styles/utils/MediaQueries.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.guide.styles.utils
2 |
3 | import io.udash.css.{CssBase, CssStyle}
4 | import io.udash.web.commons.styles.utils.StyleConstants
5 | import scalacss.internal.DslBase.ToStyle
6 |
7 | import scala.language.postfixOps
8 |
9 | object MediaQueries extends CssBase {
10 | import dsl._
11 |
12 | def desktop(properties: ToStyle*): CssStyle = mixin(
13 | media.screen.minWidth(StyleConstants.MediaQueriesBounds.TabletLandscapeMax + 1 px)(
14 | properties: _*
15 | )
16 | )
17 |
18 | def tabletLandscape(properties: ToStyle*): CssStyle = mixin(
19 | media.screen.minWidth(1 px).maxWidth(StyleConstants.MediaQueriesBounds.TabletLandscapeMax px) (
20 | properties:_*
21 | )
22 | )
23 |
24 | def tabletPortrait(properties: ToStyle*): CssStyle = mixin(
25 | media.screen.minWidth(1 px).maxWidth(StyleConstants.MediaQueriesBounds.TabletMax px) (
26 | properties:_*
27 | )
28 | )
29 |
30 | def phone(properties: ToStyle*): CssStyle = mixin(
31 | media.screen.minWidth(1 px).maxWidth(StyleConstants.MediaQueriesBounds.PhoneMax px) (
32 | properties:_*
33 | )
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/guide/shared/src/main/scala/io/udash/web/homepage/styles/HomepageDefaultStyles.scala:
--------------------------------------------------------------------------------
1 | package io.udash.web.homepage.styles
2 |
3 | import io.udash.css.CssBase
4 | import io.udash.web.commons.styles.DefaultStyles
5 | import io.udash.web.commons.styles.utils.MediaQueries
6 |
7 | import scala.language.postfixOps
8 |
9 | object HomepageDefaultStyles extends CssBase with DefaultStyles {
10 | import dsl._
11 |
12 | style(
13 | unsafeRoot("body") (
14 | fontSize(1.0625 rem)
15 | ),
16 |
17 | unsafeRoot("p")(
18 | fontSize(1 rem)
19 | ),
20 |
21 | unsafeRoot("h1") (
22 | marginBottom(3.125 rem),
23 |
24 | MediaQueries.phone(
25 | paddingTop(3.125 rem),
26 | marginBottom(1.875 rem)
27 | )
28 | )
29 | )
30 | }
31 |
--------------------------------------------------------------------------------
/i18n/.js/src/main/scala/io/udash/i18n/FrontendTranslationProvider.scala:
--------------------------------------------------------------------------------
1 | package io.udash.i18n
2 |
3 | import io.udash.logging.CrossLogging
4 |
5 | trait FrontendTranslationProvider extends TranslationProvider with CrossLogging {
6 | protected def handleMixedPlaceholders(template: String): Unit =
7 | logger.warn(s"""Indexed and unindexed placeholders in "$template"!""")
8 | }
9 |
--------------------------------------------------------------------------------
/i18n/.js/src/main/scala/io/udash/i18n/LocalTranslationProvider.scala:
--------------------------------------------------------------------------------
1 | package io.udash.i18n
2 |
3 |
4 | import scala.concurrent.Future
5 | import scala.util.Try
6 |
7 | /**
8 | * TranslationProvider dedicated to frontend-only applications.
9 | *
10 | * @param bundles `Bundle`s of translations for each language.
11 | * @param missingTranslationError This text will be used in place of missing translations.
12 | */
13 | class LocalTranslationProvider(bundles: Map[Lang, Bundle], missingTranslationError: String = "Missing translation")
14 | extends FrontendTranslationProvider {
15 |
16 | def translate(key: String, argv: Any*)(implicit lang: Lang): Future[Translated] = Future.fromTry(
17 | Try(putArgs(bundles(lang).translations.getOrElse(key, missingTranslationError), argv: _*))
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/i18n/.js/src/main/scala/io/udash/i18n/bindings/AttrTranslationModifier.scala:
--------------------------------------------------------------------------------
1 | package io.udash.i18n.bindings
2 |
3 | import com.avsystem.commons._
4 | import io.udash.i18n.Translated
5 | import io.udash.logging.CrossLogging
6 | import org.scalajs.dom.Element
7 | import scalatags.JsDom.Modifier
8 |
9 | import scala.concurrent.Future
10 | import scala.util.{Failure, Success}
11 |
12 | private[i18n] class AttrTranslationModifier(translation: => Future[Translated], attr: String)
13 | extends Modifier with CrossLogging {
14 |
15 | override def applyTo(t: Element): Unit =
16 | translation.onCompleteNow {
17 | case Success(text) =>
18 | t.setAttribute(attr, text.string)
19 | case Failure(ex) =>
20 | logger.error(ex.getMessage)
21 | }
22 | }
--------------------------------------------------------------------------------
/i18n/.js/src/main/scala/io/udash/i18n/bindings/DynamicAttrTranslationBinding.scala:
--------------------------------------------------------------------------------
1 | package io.udash.i18n.bindings
2 |
3 | import io.udash._
4 | import io.udash.bindings.modifiers.Binding
5 | import io.udash.i18n.{Lang, Translated}
6 | import org.scalajs.dom.Element
7 |
8 | import scala.concurrent.Future
9 |
10 | private[i18n] final class DynamicAttrTranslationBinding(translation: => Future[Translated], attr: String)(
11 | implicit lang: ReadableProperty[Lang]) extends AttrTranslationModifier(translation, attr) with Binding {
12 | override def applyTo(t: Element): Unit = propertyListeners += lang.listen(_ => super.applyTo(t), initUpdate = true)
13 | }
--------------------------------------------------------------------------------
/i18n/.js/src/main/scala/io/udash/i18n/bindings/DynamicTranslationBinding.scala:
--------------------------------------------------------------------------------
1 | package io.udash.i18n.bindings
2 |
3 | import com.avsystem.commons._
4 | import io.udash._
5 | import io.udash.bindings.modifiers._
6 | import io.udash.i18n._
7 | import org.scalajs.dom._
8 |
9 | import scala.concurrent.Future
10 |
11 | private[i18n] final class DynamicTranslationBinding(
12 | translation: => Future[Translated],
13 | placeholder: Option[Element],
14 | rawHtml: Boolean
15 | )(implicit lang: ReadableProperty[Lang]) extends TranslationModifier(translation, placeholder, rawHtml) with Binding {
16 | override def applyTo(t: Element): Unit = {
17 | var holder: Seq[Node] = t.appendChild(placeholder.getOrElse(emptyStringNode()))
18 | propertyListeners += lang.listen(_ => update(t, holder).foreachNow(holder = _), initUpdate = true)
19 | }
20 | }
--------------------------------------------------------------------------------
/i18n/.js/src/main/scala/io/udash/i18n/bindings/TranslationModifier.scala:
--------------------------------------------------------------------------------
1 | package io.udash.i18n.bindings
2 |
3 | import com.avsystem.commons._
4 | import io.udash.bindings.Bindings
5 | import io.udash.bindings.modifiers._
6 | import io.udash.i18n._
7 | import io.udash.logging.CrossLogging
8 | import org.scalajs.dom._
9 | import scalatags.JsDom.Modifier
10 |
11 | import scala.concurrent.Future
12 | import scala.util.{Failure, Success}
13 |
14 | private[i18n] class TranslationModifier(
15 | translation: => Future[Translated],
16 | placeholder: Option[Element],
17 | rawHtml: Boolean
18 | ) extends Modifier with CrossLogging {
19 |
20 | protected final def update(t: Element, holder: Seq[Node]): Future[Seq[Node]] = {
21 | translation.transformNow {
22 | case Success(Translated(text)) =>
23 | val newHolder: Seq[Node] = parseTranslation(rawHtml, text)
24 | t.replaceChildren(holder, newHolder)
25 | Success(newHolder)
26 | case Failure(ex) =>
27 | logger.error(ex.getMessage)
28 | Success(holder)
29 | }
30 | }
31 |
32 | override def applyTo(t: Element): Unit = {
33 | val holder: Seq[Node] = Seq(t.appendChild(placeholder.getOrElse(Bindings.emptyStringNode())))
34 | update(t, holder).discard
35 | }
36 | }
--------------------------------------------------------------------------------
/i18n/.js/src/main/scala/io/udash/i18n/bindings/package.scala:
--------------------------------------------------------------------------------
1 | package io.udash.i18n
2 |
3 | import org.scalajs.dom._
4 |
5 | import scala.scalajs.js.{JavaScriptException, SyntaxError}
6 | import scala.util.{Failure, Success, Try}
7 |
8 | package object bindings {
9 | def parseTranslation(rawHtml: Boolean, text: String): Seq[Node] =
10 | if (rawHtml) Try {
11 | val wrapper = document.createElement("div")
12 | wrapper.innerHTML = text
13 | wrapper.childNodes
14 | } match {
15 | case Success(children) if children.length > 0 =>
16 | (0 until children.length).map(children.item)
17 | case Success(_) | Failure(JavaScriptException(_: SyntaxError)) =>
18 | Seq(document.createTextNode(text))
19 | } else Seq(document.createTextNode(text))
20 | }
21 |
--------------------------------------------------------------------------------
/i18n/.js/src/main/scala/io/udash/i18n/package.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 |
3 | package object i18n extends Translations
4 |
--------------------------------------------------------------------------------
/i18n/.jvm/src/main/scala/io/udash/i18n/TranslationRPCEndpoint.scala:
--------------------------------------------------------------------------------
1 | package io.udash.i18n
2 | import scala.concurrent.{ExecutionContext, Future}
3 |
4 | /** Default implementation of `io.udash.i18n.RemoteTranslationRPC`. */
5 | class TranslationRPCEndpoint(provider: TranslationTemplatesProvider)(implicit ec: ExecutionContext) extends RemoteTranslationRPC {
6 | override def loadTemplateForLang(lang: Lang, key: String): Future[String] = Future {
7 | provider.template(key)(lang)
8 | }
9 |
10 | override def loadTranslationsForLang(lang: Lang, oldHash: BundleHash): Future[Option[Bundle]] = Future {
11 | val hash: BundleHash = provider.langHash(lang)
12 | if (hash == oldHash) None
13 | else Some(Bundle(hash, provider.allTemplates(lang)))
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/i18n/.jvm/src/main/scala/io/udash/i18n/TranslationTemplatesProvider.scala:
--------------------------------------------------------------------------------
1 | package io.udash.i18n
2 |
3 | /** Server-side translations provider. */
4 | trait TranslationTemplatesProvider {
5 | /** Returns translation template for provided `key` and `lang`. */
6 | def template(key: String)(implicit lang: Lang): String
7 |
8 | /** Returns all translation templates for provided `lang`. */
9 | def allTemplates(implicit lang: Lang): Map[String, String]
10 |
11 | /** Returns `true` if provided translations `hash` is up to date. */
12 | def langHash(implicit lang: Lang): BundleHash
13 |
14 | protected def hash(data: Map[String, String]): BundleHash =
15 | BundleHash(new String(
16 | java.security.MessageDigest.getInstance("MD5")
17 | .digest(
18 | data.map {
19 | case (key, value) => key + value
20 | }.mkString.getBytes
21 | )
22 | ))
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/i18n/.jvm/src/test/resources/mixed_en.properties:
--------------------------------------------------------------------------------
1 | mixed.first={} {1} {}
--------------------------------------------------------------------------------
/i18n/.jvm/src/test/resources/mixed_pl.properties:
--------------------------------------------------------------------------------
1 | mixed.first={} {1} {}
--------------------------------------------------------------------------------
/i18n/.jvm/src/test/resources/test2_translations_en.properties:
--------------------------------------------------------------------------------
1 | test2.key1=Key 1
2 | test2.key2=Key 2
--------------------------------------------------------------------------------
/i18n/.jvm/src/test/resources/test2_translations_pl.properties:
--------------------------------------------------------------------------------
1 | test2.key1=Klucz 1
2 | test2.key2=Klucz 2
--------------------------------------------------------------------------------
/i18n/.jvm/src/test/resources/test_translations_en.properties:
--------------------------------------------------------------------------------
1 | test1.key1=Test Key 1
2 | test1.key2=Test Key 2
--------------------------------------------------------------------------------
/i18n/.jvm/src/test/resources/test_translations_pl.properties:
--------------------------------------------------------------------------------
1 | test1.key1=Klucz testowy 1
2 | test1.key2=Klucz testowy 2
--------------------------------------------------------------------------------
/i18n/src/main/scala/io/udash/i18n/RemoteTranslationRPC.scala:
--------------------------------------------------------------------------------
1 | package io.udash.i18n
2 |
3 | import io.udash.rpc.DefaultServerRpcCompanion
4 |
5 | import scala.concurrent.Future
6 |
7 | /** RPC interface for Udash i18n handling on server-side. */
8 | trait RemoteTranslationRPC {
9 | /** Returns text to replace translation key. */
10 | def loadTemplate(key: String)(implicit lang: Lang): Future[String] =
11 | loadTemplateForLang(lang, key)
12 |
13 | /** Returns map of translations and bundle hash. If `oldHash` is not outdated, this Future will contain None. */
14 | def loadTranslations(oldHash: BundleHash)(implicit lang: Lang): Future[Option[Bundle]] =
15 | loadTranslationsForLang(lang, oldHash)
16 |
17 | /** Returns text to replace translation key. */
18 | def loadTemplateForLang(lang: Lang, key: String): Future[String]
19 |
20 | /** Returns map of translations and bundle hash. If `oldHash` is not outdated, this Future will contain None. */
21 | def loadTranslationsForLang(lang: Lang, oldHash: BundleHash): Future[Option[Bundle]]
22 | }
23 | object RemoteTranslationRPC extends DefaultServerRpcCompanion[RemoteTranslationRPC]
24 |
--------------------------------------------------------------------------------
/i18n/src/main/scala/io/udash/i18n/types.scala:
--------------------------------------------------------------------------------
1 | package io.udash.i18n
2 |
3 | import com.avsystem.commons.misc.{AbstractCase, CaseMethods}
4 | import com.avsystem.commons.serialization.{HasGenCodec, transparent}
5 |
6 | @transparent final case class Lang(lang: String) extends AnyVal with CaseMethods
7 | object Lang extends HasGenCodec[Lang]
8 |
9 | @transparent final case class BundleHash(hash: String) extends AnyVal with CaseMethods
10 | object BundleHash extends HasGenCodec[BundleHash]
11 |
12 | final case class Bundle(hash: BundleHash, translations: Map[String, String]) extends AbstractCase
13 | object Bundle extends HasGenCodec[Bundle]
14 |
15 | @transparent final case class Translated(string: String) extends AnyVal with CaseMethods
16 | object Translated extends HasGenCodec[Translated]
--------------------------------------------------------------------------------
/i18n/src/test/scala/io/udash/i18n/Utils.scala:
--------------------------------------------------------------------------------
1 | package io.udash.i18n
2 |
3 | object Utils {
4 | def getTranslatedString(tr: TranslationKey0)(implicit lang: Lang, provider: TranslationProvider): String =
5 | tr().value.get.get.string
6 | }
7 |
--------------------------------------------------------------------------------
/macros/src/main/scala/io/udash/macros/ComponentIdMacro.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package macros
3 |
4 | import scala.reflect.macros.blackbox
5 |
6 | class ComponentIdMacro(val c: blackbox.Context) {
7 |
8 | import c.universe._
9 |
10 | final def IdObj: Tree = q"io.udash.component.ComponentId"
11 |
12 | def impl(): c.Tree = {
13 | val fqn = Iterator.iterate(c.internal.enclosingOwner)(_.owner).find(_.isClass).get.fullName.replace('.', '-')
14 | q"$IdObj.forName($fqn)"
15 | }
16 | }
--------------------------------------------------------------------------------
/macros/src/main/scala/io/udash/macros/RestMacros.scala:
--------------------------------------------------------------------------------
1 | package io.udash.macros
2 |
3 | import com.avsystem.commons.macros.AbstractMacroCommons
4 |
5 | import scala.reflect.macros.blackbox
6 |
7 | class RestMacros(val ctx: blackbox.Context) extends AbstractMacroCommons(ctx) {
8 |
9 | import c.universe._
10 |
11 | def UdashRestPkg: Tree = q"_root_.io.udash.rest"
12 |
13 | def materializeImplMetadata[Real: c.WeakTypeTag]: Tree =
14 | q"$CommonsPkg.rpc.RpcMetadata.materializeForApi[$UdashRestPkg.raw.RestMetadata, ${weakTypeOf[Real]}]"
15 |
16 | def materializeImplOpenApiMetadata[Real: c.WeakTypeTag]: Tree =
17 | q"$CommonsPkg.rpc.RpcMetadata.materializeForApi[$UdashRestPkg.openapi.OpenApiMetadata, ${weakTypeOf[Real]}]"
18 | }
19 |
--------------------------------------------------------------------------------
/macros/src/main/scala/io/udash/macros/TestMacros.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package macros
3 |
4 | import com.avsystem.commons.macros.AbstractMacroCommons
5 |
6 | import scala.reflect.macros.{TypecheckException, blackbox}
7 |
8 | class TestMacros(val ctx: blackbox.Context) extends AbstractMacroCommons(ctx) {
9 |
10 | import c.universe._
11 |
12 | private def stringLiteral(tree: Tree): String = tree match {
13 | case StringLiteral(str) => str
14 | case Select(StringLiteral(str), TermName("stripMargin")) => str.stripMargin
15 | case _ => abort(s"expected string literal, got $tree")
16 | }
17 |
18 | def typeErrorImpl(code: Tree): Tree = {
19 | val codeTree = c.parse(stringLiteral(code))
20 | try {
21 | c.typecheck(codeTree)
22 | abort("expected typechecking error, none was raised")
23 | } catch {
24 | case TypecheckException(_, msg) => q"$msg"
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "jsdom": "^24.1.0",
4 | "ws": "^8.17.1"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/project/build.properties:
--------------------------------------------------------------------------------
1 | # suppress inspection "UnusedProperty"
2 | sbt.version=1.11.1
3 |
--------------------------------------------------------------------------------
/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | logLevel := Level.Warn
2 |
3 | libraryDependencies += "org.scala-js" %% "scalajs-env-selenium" % "1.1.1"
4 | libraryDependencies += "org.scala-js" %% "scalajs-env-jsdom-nodejs" % "1.1.0"
5 |
6 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.19.0")
7 | addSbtPlugin("org.scala-js" % "sbt-jsdependencies" % "1.0.2")
8 | addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.6.4")
9 | addSbtPlugin("org.jetbrains.scala" % "sbt-ide-settings" % "1.1.2")
10 | addSbtPlugin("com.github.sbt" % "sbt-less" % "2.0.1")
11 | addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.11.1")
12 | addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.11.1")
13 |
--------------------------------------------------------------------------------
/rest/.js/src/main/scala/io/udash/rest/DefaultSttpBackend.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package rest
3 |
4 | import sttp.client3.{FetchBackend, SttpBackend}
5 |
6 | import scala.concurrent.Future
7 |
8 | object DefaultSttpBackend {
9 | def apply(): SttpBackend[Future, Any] = FetchBackend()
10 | }
11 |
--------------------------------------------------------------------------------
/rest/.jvm/src/main/scala/io/udash/rest/DefaultSttpBackend.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package rest
3 |
4 | import sttp.client3.{HttpClientFutureBackend, SttpBackend}
5 |
6 | import scala.concurrent.Future
7 |
8 | object DefaultSttpBackend {
9 | def apply(): SttpBackend[Future, Any] = HttpClientFutureBackend()
10 | }
11 |
--------------------------------------------------------------------------------
/rest/.jvm/src/main/scala/io/udash/rest/openapi/OpenApiServlet.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package rest.openapi
3 |
4 | import com.avsystem.commons.OptArg
5 | import com.avsystem.commons.annotation.explicitGenerics
6 | import com.avsystem.commons.serialization.json.JsonStringOutput
7 | import javax.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse}
8 |
9 | object OpenApiServlet {
10 | @explicitGenerics def apply[RestApi: OpenApiMetadata](
11 | info: Info,
12 | components: Components = Components(),
13 | servers: List[Server] = Nil,
14 | security: List[SecurityRequirement] = Nil,
15 | tags: List[Tag] = Nil,
16 | externalDocs: OptArg[ExternalDocumentation] = OptArg.Empty
17 | ): OpenApiServlet = new OpenApiServlet {
18 | protected def render(request: HttpServletRequest): OpenApi =
19 | implicitly[OpenApiMetadata[RestApi]].openapi(info, components, servers, security, tags, externalDocs)
20 | }
21 | }
22 |
23 | abstract class OpenApiServlet extends HttpServlet {
24 | protected def render(request: HttpServletRequest): OpenApi
25 |
26 | override def doGet(req: HttpServletRequest, resp: HttpServletResponse): Unit = {
27 | resp.setContentType("application/json;charset=utf-8")
28 | resp.getWriter.write(JsonStringOutput.writePretty(render(req)))
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/rest/.jvm/src/test/scala/io/udash/rest/ServletBasedRestApiTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package rest
3 |
4 | import org.eclipse.jetty.ee8.servlet.{ServletContextHandler, ServletHolder}
5 | import org.eclipse.jetty.server.Server
6 |
7 | import scala.concurrent.duration.*
8 |
9 | abstract class ServletBasedRestApiTest extends RestApiTest with UsesHttpServer {
10 | override implicit val patienceConfig: PatienceConfig = PatienceConfig(10.seconds)
11 |
12 | def maxPayloadSize: Int = 1024 * 1024
13 | def serverTimeout: FiniteDuration = 10.seconds
14 |
15 | protected def setupServer(server: Server): Unit = {
16 | val servlet = new RestServlet(serverHandle, serverTimeout, maxPayloadSize)
17 | val holder = new ServletHolder(servlet)
18 | val handler = new ServletContextHandler()
19 | handler.addServlet(holder, "/api/*")
20 | server.setHandler(handler)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/rest/.jvm/src/test/scala/io/udash/rest/SomeApi.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package rest
3 |
4 | import scala.concurrent.Future
5 |
6 | trait SomeApi {
7 | @GET
8 | def hello(who: String): Future[String]
9 |
10 | @POST("hello")
11 | def helloThere(who: String): Future[String]
12 | }
13 |
14 | object SomeApi extends DefaultRestApiCompanion[SomeApi] {
15 | def format(who: String) = s"Hello, $who!"
16 | val poison: String = "poison"
17 |
18 | val impl: SomeApi = new SomeApi {
19 | override def hello(who: String): Future[String] = {
20 | if (who == poison) throw new IllegalArgumentException(poison)
21 | else Future.successful(format(who))
22 | }
23 |
24 | override def helloThere(who: String): Future[String] = hello(who)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/rest/.jvm/src/test/scala/io/udash/rest/examples/ClientMain.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package rest.examples
3 |
4 | import io.udash.rest.SttpRestClient
5 | import sttp.client3.SttpBackend
6 |
7 | import scala.concurrent.duration._
8 | import scala.concurrent.{Await, Future}
9 | import scala.util.{Failure, Success}
10 |
11 | object ClientMain {
12 | def main(args: Array[String]): Unit = {
13 | implicit val sttpBackend: SttpBackend[Future, Any] = SttpRestClient.defaultBackend()
14 | val proxy: UserApi = SttpRestClient[UserApi, Future]("http://localhost:9090")
15 |
16 | // make a remote REST call
17 | val result: Future[User] = proxy.createUser("Fred")
18 |
19 | // use whatever execution context is appropriate
20 | import scala.concurrent.ExecutionContext.Implicits.global
21 |
22 | result.onComplete {
23 | case Success(user) => println(s"User $user created")
24 | case Failure(cause) => cause.printStackTrace()
25 | }
26 |
27 | // just wait until future is complete so that main thread doesn't finish prematurely
28 | Await.ready(result, 10.seconds)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/rest/.jvm/src/test/scala/io/udash/rest/examples/GenericApi.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package rest.examples
3 |
4 | import io.udash.rest._
5 | import io.udash.rest.raw._
6 | import com.avsystem.commons.serialization.GenCodec
7 |
8 | import scala.concurrent.Future
9 |
10 | trait GenericApi[T] {
11 | def process(value: T): Future[T]
12 | }
13 | object GenericApi {
14 | import DefaultRestImplicits._
15 | implicit def restAsRawReal[T: GenCodec]: RawRest.AsRawRealRpc[GenericApi[T]] = RawRest.materializeAsRawReal
16 | implicit def restMetadata[T]: RestMetadata[GenericApi[T]] = RestMetadata.materialize
17 |
18 | import openapi._
19 | implicit def openApiMetadata[T: RestSchema]: OpenApiMetadata[GenericApi[T]] = OpenApiMetadata.materialize
20 | }
--------------------------------------------------------------------------------
/rest/.jvm/src/test/scala/io/udash/rest/examples/ServerMain.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package rest.examples
3 |
4 | import io.udash.rest.RestServlet
5 | import monix.execution.Scheduler
6 | import org.eclipse.jetty.server.Server
7 | import org.eclipse.jetty.ee8.servlet.{ServletContextHandler, ServletHolder}
8 |
9 | import scala.concurrent.Future
10 |
11 | class UserApiImpl extends UserApi {
12 | def createUser(name: String): Future[User] =
13 | Future.successful(User(UserId(0), name))
14 | def getUser(id: UserId): Future[User] =
15 | Future.successful(User(id, s"$id-name"))
16 | }
17 |
18 | object ServerMain {
19 | def main(args: Array[String]): Unit = {
20 | // Scheduler.global is usually not the best choice
21 | // use whatever Scheduler is appropriate in your application, e.g. freshly created Scheduler.computation()
22 | implicit val scheduler: Scheduler = Scheduler.global
23 |
24 | val server = new Server(9090)
25 | val handler = new ServletContextHandler
26 | handler.addServlet(new ServletHolder(RestServlet[UserApi](new UserApiImpl)), "/*")
27 | server.setHandler(handler)
28 | server.start()
29 | server.join()
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/rest/.jvm/src/test/scala/io/udash/rest/openapi/OpenApiGenerationTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package rest.openapi
3 |
4 | import com.avsystem.commons.serialization.json.JsonStringOutput
5 | import io.udash.rest.RestTestApi
6 |
7 | import scala.io.Source
8 | import org.scalatest.funsuite.AnyFunSuite
9 |
10 | class OpenApiGenerationTest extends AnyFunSuite {
11 | test("openapi for RestTestApi") {
12 | val openapi = RestTestApi.openapiMetadata.openapi(
13 | Info("Test API", "0.1", description = "Some test REST API"),
14 | servers = List(Server("http://localhost"))
15 | )
16 | val expected = Source.fromInputStream(getClass.getResourceAsStream("/RestTestApi.json")).getLines().mkString("\n")
17 | val actual = JsonStringOutput.writePretty(openapi)
18 | assert(actual == expected)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/rest/jetty/src/test/scala/io/udash/rest/jetty/JettyRestCallTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package rest.jetty
3 |
4 | import io.udash.rest.raw.RawRest.HandleRequest
5 | import io.udash.rest.{RestApiTestScenarios, ServletBasedRestApiTest}
6 | import org.eclipse.jetty.client.HttpClient
7 |
8 | final class JettyRestCallTest extends ServletBasedRestApiTest with RestApiTestScenarios {
9 | /**
10 | * Similar to the default HttpClient, but with a connection timeout
11 | * significantly exceeding the value of the CallTimeout
12 | */
13 | val client: HttpClient = new HttpClient() {
14 | setMaxConnectionsPerDestination(MaxConnections)
15 | setIdleTimeout(IdleTimout.toMillis)
16 | }
17 |
18 | def clientHandle: HandleRequest =
19 | JettyRestClient.asHandleRequest(client, s"$baseUrl/api", maxPayloadSize)
20 |
21 | override protected def beforeAll(): Unit = {
22 | super.beforeAll()
23 | client.start()
24 | }
25 |
26 | override protected def afterAll(): Unit = {
27 | client.stop()
28 | super.afterAll()
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/rest/src/main/scala/io/udash/rest/RestException.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package rest
3 |
4 | import com.avsystem.commons.rpc.InvalidRpcCall
5 |
6 | class RestException(msg: String, cause: Throwable = null) extends InvalidRpcCall(msg, cause)
7 |
8 | class InvalidRestApiException(msg: String) extends RestException(msg)
9 |
--------------------------------------------------------------------------------
/rest/src/main/scala/io/udash/rest/openapi/WhenAbsentInfo.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package rest.openapi
3 |
4 | import com.avsystem.commons._
5 | import com.avsystem.commons.meta.{TypedMetadata, infer, reifyAnnot}
6 | import com.avsystem.commons.rpc.AsRaw
7 | import com.avsystem.commons.serialization.whenAbsent
8 | import io.udash.rest.raw.JsonValue
9 |
10 | import scala.util.Try
11 |
12 | final case class WhenAbsentInfo[T](
13 | @reifyAnnot annot: whenAbsent[T],
14 | @infer("for @whenAbsent value: ") asJson: AsRaw[JsonValue, T]
15 | ) extends TypedMetadata[T] {
16 | val fallbackValue: Opt[JsonValue] =
17 | Try(annot.value).fold(_ => Opt.Empty, v => asJson.asRaw(v).opt)
18 | }
19 |
--------------------------------------------------------------------------------
/rest/src/main/scala/io/udash/rest/raw/JsonValue.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package rest.raw
3 |
4 | import com.avsystem.commons.serialization.GenCodec
5 | import com.avsystem.commons.serialization.json.RawJson
6 |
7 | /**
8 | * Value used as encoding of [[io.udash.rest.Body Body]] parameters of
9 | * [[io.udash.rest.JsonBody JsonBody]] methods. Wrapped value MUST be a valid JSON.
10 | */
11 | final case class JsonValue(value: String) extends AnyVal
12 | object JsonValue extends (String => JsonValue) {
13 | implicit val codec: GenCodec[JsonValue] = GenCodec.create(
14 | i => JsonValue(i.readCustom(RawJson).getOrElse(i.readSimple().readString())),
15 | (o, v) => if (!o.writeCustom(RawJson, v.value)) o.writeSimple().writeString(v.value)
16 | )
17 |
18 | final val Null = JsonValue("null")
19 | }
20 |
--------------------------------------------------------------------------------
/rest/src/test/scala/io/udash/rest/PolyRestApi.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package rest
3 |
4 | trait PolyRestApi[F[_]] {
5 | def postThis(thing: String): F[Int]
6 | }
7 | object PolyRestApi extends DefaultPolyRestApiCompanion[PolyRestApi]
8 |
--------------------------------------------------------------------------------
/rest/src/test/scala/io/udash/rest/PolyRestDataCompanion.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package rest
3 |
4 | import com.avsystem.commons.meta.MacroInstances
5 | import com.avsystem.commons.serialization.GenCodec
6 | import io.udash.rest.openapi.{RestSchema, RestStructure}
7 |
8 | trait PolyCodecWithStructure[C[_]] {
9 | def codec[T0: GenCodec]: GenCodec[C[T0]]
10 | def structure[T0: RestSchema]: RestStructure[C[T0]]
11 | }
12 |
13 | abstract class PolyRestDataCompanion[C[_]](implicit
14 | instances: MacroInstances[DefaultRestImplicits, PolyCodecWithStructure[C]]
15 | ) extends {
16 | implicit def codec[T: GenCodec]: GenCodec[C[T]] = instances(DefaultRestImplicits, this).codec
17 | implicit def restStructure[T: RestSchema]: RestStructure[C[T]] = instances(DefaultRestImplicits, this).structure
18 | implicit def restSchema[T: RestSchema]: RestSchema[C[T]] = RestSchema.lazySchema(restStructure[T].standaloneSchema)
19 | }
20 |
--------------------------------------------------------------------------------
/rest/src/test/scala/io/udash/rest/SomeServerApiImpl.scala:
--------------------------------------------------------------------------------
1 | package io.udash.rest
2 |
3 | import scala.concurrent.Future
4 |
5 | final class SomeServerApiImpl {
6 | @GET
7 | def thingy(param: Int): Future[String] = Future.successful((param - 1).toString)
8 |
9 | val subapi = new SomeServerSubApiImpl
10 | }
11 | object SomeServerApiImpl extends DefaultRestServerApiImplCompanion[SomeServerApiImpl]
12 |
13 | final class SomeServerSubApiImpl {
14 | @POST
15 | def yeet(data: String): Future[String] = Future.successful(s"yeet $data")
16 | }
17 | object SomeServerSubApiImpl extends DefaultRestServerApiImplCompanion[SomeServerSubApiImpl]
18 |
--------------------------------------------------------------------------------
/rest/src/test/scala/io/udash/rest/TestRESTRecord.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package rest
3 |
4 | case class TestRESTRecord(id: Option[Int], s: String)
5 | object TestRESTRecord extends RestDataCompanion[TestRESTRecord]
--------------------------------------------------------------------------------
/rest/src/test/scala/io/udash/rest/WriteOpenApi.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package rest
3 |
4 | import java.io.FileWriter
5 |
6 | import com.avsystem.commons.serialization.json.{JsonOptions, JsonStringOutput}
7 | import io.udash.rest.openapi.{Info, Server}
8 |
9 | object WriteOpenApi {
10 | def main(args: Array[String]): Unit = {
11 | val openapi = RestTestApi.openapiMetadata.openapi(
12 | Info("Test API", "0.1", description = "Some test REST API"),
13 | servers = List(Server("http://localhost"))
14 | )
15 | val fw = new FileWriter("/home/ghik/api.js")
16 | fw.write("apiSpec = ")
17 | fw.write(JsonStringOutput.write(openapi, JsonOptions.Pretty))
18 | fw.close()
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/rest/src/test/scala/io/udash/rest/openapi/customWa.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package rest.openapi
3 |
4 | import com.avsystem.commons.annotation.AnnotationAggregate
5 | import com.avsystem.commons.serialization.whenAbsent
6 |
7 | import scala.annotation.StaticAnnotation
8 |
9 | class customWa[+T](value: => T) extends AnnotationAggregate {
10 | @whenAbsent(value)
11 | final def aggregated: List[StaticAnnotation] = reifyAggregated
12 | }
13 |
--------------------------------------------------------------------------------
/rest/src/test/scala/io/udash/rest/raw/MappingTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package rest.raw
3 |
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class MappingTest extends AnyFunSuite {
7 | test("simple") {
8 | assert(Mapping.create("a" -> 1).toMap == Map("a" -> 1))
9 | }
10 |
11 | test("repeated key") {
12 | assert(Mapping.create("a" -> 1, "a" -> 2).toMap == Map("a" -> 2))
13 | }
14 |
15 | test("case sensitive") {
16 | assert(Mapping.create("a" -> 1, "A" -> 2).toMap == Map("a" -> 1, "A" -> 2))
17 | }
18 |
19 | test("append") {
20 | assert(Mapping.create("a" -> 1).append("b", 2) == Mapping.create("a" -> 1, "b" -> 2))
21 | }
22 |
23 | test("prepend") {
24 | assert(Mapping.create("a" -> 1).prepend("b", 2) == Mapping.create("b" -> 2, "a" -> 1))
25 | }
26 |
27 | test("case insensitive") {
28 | assert(IMapping.create("a" -> 1, "A" -> 2).toMap == Map("a" -> 2))
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/rpc/.js/src/main/scala/io/udash/rpc/ConnectionStatus.scala:
--------------------------------------------------------------------------------
1 | package io.udash.rpc
2 |
3 | import com.avsystem.commons.misc.{AbstractValueEnum, EnumCtx, ValueEnumCompanion}
4 |
5 | final class ConnectionStatus(implicit enumCtx: EnumCtx) extends AbstractValueEnum
6 | object ConnectionStatus extends ValueEnumCompanion[ConnectionStatus] {
7 | final val Open, Closed: Value = new ConnectionStatus
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/rpc/.js/src/main/scala/io/udash/rpc/RPCFrontend.scala:
--------------------------------------------------------------------------------
1 | package io.udash.rpc
2 |
3 | trait RPCFrontend {
4 | type ServerConnector = io.udash.rpc.internals.ServerConnector
5 | type AtmosphereServerConnector = io.udash.rpc.internals.AtmosphereServerConnector
6 | type DefaultAtmosphereServerConnector = io.udash.rpc.internals.DefaultAtmosphereServerConnector
7 | type ExposesClientRPC[ClientRPCType] = io.udash.rpc.internals.ExposesClientRPC[ClientRPCType]
8 | type DefaultExposesClientRPC[ClientRPCType] = io.udash.rpc.internals.DefaultExposesClientRPC[ClientRPCType]
9 | }
10 |
--------------------------------------------------------------------------------
/rpc/.js/src/main/scala/io/udash/rpc/internals/ExposesClientRPC.scala:
--------------------------------------------------------------------------------
1 | package io.udash.rpc.internals
2 |
3 | import io.udash.rpc._
4 |
5 | abstract class ExposesClientRPC[ClientRPCType](protected val localRpc: ClientRPCType)
6 | extends ExposesLocalRPC[ClientRPCType] {
7 | /**
8 | * This allows the RPC implementation to be wrapped in raw RPC which will translate raw calls coming from network
9 | * into calls on actual RPC implementation.
10 | */
11 | protected def localRpcAsRaw: ClientRawRpc.AsRawRpc[ClientRPCType]
12 |
13 | protected lazy val rawLocalRpc: ClientRawRpc = localRpcAsRaw.asRaw(localRpc)
14 |
15 | /** Handles RPCFires */
16 | def handleRpcFire(fire: RpcFire): Unit =
17 | rawLocalRpc.handleFire(fire)
18 | }
19 |
20 | class DefaultExposesClientRPC[ClientRPCType](local: ClientRPCType)(
21 | implicit protected val localRpcAsRaw: ClientRawRpc.AsRawRpc[ClientRPCType]
22 | ) extends ExposesClientRPC(local)
23 |
--------------------------------------------------------------------------------
/rpc/.js/src/main/scala/io/udash/rpc/package.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 |
3 | package object rpc extends RPCFrontend
4 |
--------------------------------------------------------------------------------
/rpc/.js/src/main/scala/io/udash/rpc/serialization/DefaultExceptionCodecRegistry.scala:
--------------------------------------------------------------------------------
1 | package io.udash.rpc.serialization
2 |
3 | class DefaultExceptionCodecRegistry extends ClassNameBasedECR {
4 | override def name[T <: Throwable](ex: T): String =
5 | throw new NotImplementedError("This method is implemented only in the JVM version.")
6 | }
--------------------------------------------------------------------------------
/rpc/.js/src/test/scala/io/udash/rpc/NativeRpcMessagesTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash.rpc
2 |
3 | class NativeRpcMessagesTest extends RpcMessagesTestScenarios {
4 | "RPCMessages default JS serializers" should tests()
5 | "RPCMessages default JS serializers" should hugeTests()
6 | }
7 |
--------------------------------------------------------------------------------
/rpc/.jvm/src/main/scala/io/udash/rpc/ExposesServerRPC.scala:
--------------------------------------------------------------------------------
1 | package io.udash.rpc
2 |
3 | import scala.concurrent.Future
4 |
5 | abstract class ExposesServerRPC[ServerRPCType](local: ServerRPCType) extends ExposesLocalRPC[ServerRPCType] {
6 | /**
7 | * This allows the RPC implementation to be wrapped in raw RPC which will translate raw calls coming from network
8 | * into calls on actual RPC implementation.
9 | */
10 | protected def localRpcAsRaw: ServerRawRpc.AsRawRpc[ServerRPCType]
11 |
12 | protected lazy val rawLocalRpc: ServerRawRpc =
13 | localRpcAsRaw.asRaw(localRpc)
14 |
15 | override protected def localRpc: ServerRPCType = local
16 |
17 | /** Handles RPCCall and returns Future with call result. */
18 | def handleRpcCall(call: RpcCall): Future[JsonStr] =
19 | rawLocalRpc.handleCall(call)
20 |
21 | /** Handles RPCFire */
22 | def handleRpcFire(fire: RpcFire): Unit =
23 | rawLocalRpc.handleFire(fire)
24 | }
25 |
26 | class DefaultExposesServerRPC[ServerRPCType](local: ServerRPCType)(
27 | implicit protected val localRpcAsRaw: ServerRawRpc.AsRawRpc[ServerRPCType]
28 | ) extends ExposesServerRPC(local)
29 |
--------------------------------------------------------------------------------
/rpc/.jvm/src/main/scala/io/udash/rpc/RPCBackend.scala:
--------------------------------------------------------------------------------
1 | package io.udash.rpc
2 |
3 | trait RPCBackend {
4 | type DefaultAtmosphereFramework = io.udash.rpc.utils.DefaultAtmosphereFramework
5 | type FileDownloadServlet = io.udash.rpc.utils.FileDownloadServlet
6 | type FileUploadServlet = io.udash.rpc.utils.FileUploadServlet
7 | }
--------------------------------------------------------------------------------
/rpc/.jvm/src/main/scala/io/udash/rpc/RpcServlet.scala:
--------------------------------------------------------------------------------
1 | package io.udash.rpc
2 |
3 | import javax.servlet.ServletConfig
4 | import javax.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse}
5 | import org.atmosphere.cpr._
6 |
7 | /**
8 | * Servlet for RPC endpoint.
9 | *
10 | * @param framework Instance of initialized AtmosphereFramework, which handle RPC requests.
11 | */
12 | class RpcServlet(framework: AtmosphereFramework) extends HttpServlet {
13 | override def init(config: ServletConfig): Unit = {
14 | super.init(config)
15 | framework.init(config)
16 | }
17 |
18 | override def doTrace(req: HttpServletRequest, resp: HttpServletResponse): Unit = doPost(req, resp)
19 |
20 | override def doGet(req: HttpServletRequest, resp: HttpServletResponse): Unit = doPost(req, resp)
21 |
22 | override def doPut(req: HttpServletRequest, resp: HttpServletResponse): Unit = doPost(req, resp)
23 |
24 | override def doPost(req: HttpServletRequest, resp: HttpServletResponse): Unit = {
25 | framework.doCometSupport(AtmosphereRequestImpl.wrap(req), AtmosphereResponseImpl.wrap(resp))
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/rpc/.jvm/src/main/scala/io/udash/rpc/internals/UsesClientRPC.scala:
--------------------------------------------------------------------------------
1 | package io.udash.rpc.internals
2 |
3 | import io.udash.rpc._
4 |
5 | /**
6 | * Base class for server-side components which use some RPC exposed by client-side.
7 | */
8 | private[rpc] abstract class UsesClientRPC[ClientRPCType] extends UsesRemoteRPC[ClientRPCType] {
9 | /**
10 | * Proxy for remote RPC implementation. Use this to perform RPC calls.
11 | */
12 | lazy val remoteRpc: ClientRPCType =
13 | remoteRpcAsReal.asReal(new RawRemoteRPC(Nil))
14 |
15 | /**
16 | * This allows for generation of proxy which translates RPC calls into raw calls that
17 | * can be sent through the network.
18 | */
19 | protected def remoteRpcAsReal: ClientRawRpc.AsRealRpc[ClientRPCType]
20 |
21 | protected class RawRemoteRPC(getterChain: List[RpcInvocation]) extends ClientRawRpc {
22 | def fire(invocation: RpcInvocation): Unit =
23 | fireRemote(getterChain, invocation)
24 |
25 | def get(invocation: RpcInvocation): ClientRawRpc =
26 | new RawRemoteRPC(invocation :: getterChain)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/rpc/.jvm/src/main/scala/io/udash/rpc/package.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 |
3 | package object rpc extends RPCBackend
4 |
--------------------------------------------------------------------------------
/rpc/.jvm/src/main/scala/io/udash/rpc/serialization/DefaultExceptionCodecRegistry.scala:
--------------------------------------------------------------------------------
1 | package io.udash.rpc.serialization
2 |
3 | class DefaultExceptionCodecRegistry extends ClassNameBasedECR {
4 | override def name[T <: Throwable](ex: T): String = {
5 | import com.avsystem.commons._
6 | def find(cls: Class[_]): Opt[String] = {
7 | if (cls == null) Opt.Empty
8 | else if (codecs.contains(cls.getName)) Opt(cls.getName)
9 | else {
10 | cls.getInterfaces.iterator
11 | .flatMap(find)
12 | .filter(_.nonEmpty)
13 | .nextOpt
14 | .orElse(find(cls.getSuperclass))
15 | }
16 | }
17 | find(ex.getClass).getOrElse(ex.getClass.getName)
18 | }
19 | }
--------------------------------------------------------------------------------
/rpc/.jvm/src/main/scala/io/udash/rpc/utils/CallLogging.scala:
--------------------------------------------------------------------------------
1 | package io.udash.rpc.utils
2 |
3 | import io.udash.rpc._
4 |
5 | import scala.concurrent.Future
6 |
7 | /**
8 | * ExposesServerRPC mixin simplifying RPC calls logging.
9 | */
10 | trait CallLogging[ServerRPCType] extends ExposesServerRPC[ServerRPCType] {
11 |
12 | protected val metadata: ServerRpcMetadata[ServerRPCType]
13 |
14 | protected def logAll: Boolean = false
15 |
16 | def log(rpcName: String, methodName: String, args: Seq[String]): Unit
17 |
18 | private def handleRpcRequest(msg: RpcRequest): Unit = {
19 | val classMetadata =
20 | msg.gettersChain.reverseIterator.foldLeft[ServerRpcMetadata[_]](metadata) {
21 | (metadata, invocation) => metadata.getters(invocation.rpcName).resultMetadata.value
22 | }
23 |
24 | val methodMetadata = classMetadata.methods(msg.invocation.rpcName)
25 |
26 | if (logAll || methodMetadata.logged)
27 | log(classMetadata.name, methodMetadata.name, msg.invocation.args.map(_.json))
28 | }
29 |
30 | override def handleRpcFire(fire: RpcFire): Unit = {
31 | handleRpcRequest(fire)
32 | super.handleRpcFire(fire)
33 | }
34 |
35 | override def handleRpcCall(call: RpcCall): Future[JsonStr] = {
36 | handleRpcRequest(call)
37 | super.handleRpcCall(call)
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/rpc/.jvm/src/main/scala/io/udash/rpc/utils/FileUploadServlet.scala:
--------------------------------------------------------------------------------
1 | package io.udash.rpc.utils
2 |
3 | import java.io.InputStream
4 | import java.nio.file.Paths
5 | import javax.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse}
6 | import com.avsystem.commons._
7 |
8 | /** Template of a servlet handling files upload. It takes files from the request and passes data to the `handleFile` method.
9 | * @param fileFields Names of file inputs in the HTTP request. */
10 | abstract class FileUploadServlet(fileFields: Set[String]) extends HttpServlet {
11 | /** Uploaded file handler. */
12 | protected def handleFile(name: String, content: InputStream): Unit
13 |
14 | override protected def doPost(request: HttpServletRequest, response: HttpServletResponse): Unit = {
15 | request.getParts.asScala
16 | .filter(part => fileFields.contains(part.getName))
17 | .foreach(filePart => {
18 | val fileName = Paths.get(filePart.getSubmittedFileName).getFileName.toString
19 | val fileContent = filePart.getInputStream
20 | handleFile(fileName, fileContent)
21 | fileContent.close()
22 | })
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/rpc/.jvm/src/test/scala/io/udash/rpc/JVMSerializationIntegrationTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash.rpc
2 |
3 | class JVMSerializationIntegrationTest extends SerializationIntegrationTestBase {
4 | "DefaultUdashRPCFramework -> DefaultUdashRPCFramework default serialization" should tests
5 | }
--------------------------------------------------------------------------------
/rpc/.jvm/src/test/scala/io/udash/rpc/RpcMessagesTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash.rpc
2 |
3 | class RpcMessagesTest extends RpcMessagesTestScenarios {
4 | "RPCMessages default serializers" should tests()
5 | "RPCMessages default serializers" should hugeTests()
6 | }
--------------------------------------------------------------------------------
/rpc/src/main/scala/io/udash/rpc/ExposesLocalRPC.scala:
--------------------------------------------------------------------------------
1 | package io.udash.rpc
2 |
3 | /**
4 | * Base trait for anything that exposes some RPC interface.
5 | */
6 | trait ExposesLocalRPC[T] {
7 | /**
8 | * Implementation of local RPC interface. Common approach is to implement the local RPC directly and
9 | * return reference to `this` here.
10 | */
11 | protected def localRpc: T
12 | }
13 |
--------------------------------------------------------------------------------
/rpc/src/main/scala/io/udash/rpc/UsesRemoteRPC.scala:
--------------------------------------------------------------------------------
1 | package io.udash.rpc
2 |
3 | /**
4 | * Base trait for anything that uses remote RPC interface.
5 | */
6 | trait UsesRemoteRPC[T] {
7 | /**
8 | * Sends the raw RPC invocation of method returning `Unit` through network.
9 | */
10 | protected def fireRemote(getterChain: List[RpcInvocation], invocation: RpcInvocation): Unit
11 | }
12 |
--------------------------------------------------------------------------------
/rpc/src/main/scala/io/udash/rpc/serialization/DefaultUdashSerialization.scala:
--------------------------------------------------------------------------------
1 | package io.udash.rpc.serialization
2 |
3 | import com.avsystem.commons.meta.Fallback
4 | import com.avsystem.commons.misc.ImplicitNotFound
5 | import com.avsystem.commons.rpc.{AsRaw, AsReal}
6 | import com.avsystem.commons.serialization.GenCodec
7 | import com.avsystem.commons.serialization.json.{JsonStringInput, JsonStringOutput}
8 | import io.udash.rpc.JsonStr
9 |
10 | import scala.annotation.implicitNotFound
11 |
12 | trait DefaultUdashSerialization {
13 | implicit def genCodecBasedAsReal[T: GenCodec]: Fallback[AsReal[JsonStr, T]] =
14 | Fallback(jsonStr => JsonStringInput.read[T](jsonStr.json))
15 |
16 | implicit def genCodecBasedAsRaw[T: GenCodec]: Fallback[AsRaw[JsonStr, T]] =
17 | Fallback(value => JsonStr(JsonStringOutput.write[T](value)))
18 |
19 | @implicitNotFound("#{forGenCodec}")
20 | implicit def asRealNotFound[T](
21 | implicit forGenCodec: ImplicitNotFound[GenCodec[T]]
22 | ): ImplicitNotFound[AsReal[JsonStr, T]] = ImplicitNotFound()
23 |
24 | @implicitNotFound("#{forGenCodec}")
25 | implicit def asRawNotFound[T](
26 | implicit forGenCodec: ImplicitNotFound[GenCodec[T]]
27 | ): ImplicitNotFound[AsRaw[JsonStr, T]] = ImplicitNotFound()
28 | }
29 | object DefaultUdashSerialization extends DefaultUdashSerialization
30 |
--------------------------------------------------------------------------------
/rpc/src/main/scala/io/udash/rpc/utils/Logged.scala:
--------------------------------------------------------------------------------
1 | package io.udash.rpc.utils
2 |
3 | import scala.annotation.StaticAnnotation
4 |
5 | /**
6 | * Marks RPC method for CallLogging
7 | */
8 | class Logged extends StaticAnnotation
9 |
--------------------------------------------------------------------------------
/rpc/src/test/scala/io/udash/rpc/types.scala:
--------------------------------------------------------------------------------
1 | package io.udash.rpc
2 |
3 | import com.avsystem.commons.serialization.HasGenCodec
4 |
5 | case class TestCC(i: Int, l: Long, intAsDouble: Double, b: Boolean, s: String, list: List[Char])
6 | object TestCC extends HasGenCodec[TestCC]
7 |
8 | case class NestedTestCC(i: Int, t: TestCC, t2: TestCC)
9 | object NestedTestCC extends HasGenCodec[NestedTestCC]
10 |
11 | case class DeepNestedTestCC(n: NestedTestCC, l: DeepNestedTestCC)
12 | object DeepNestedTestCC extends HasGenCodec[DeepNestedTestCC]
13 |
14 | case class CompleteItem(unit: Unit, string: String, specialString: String, char: Char, boolean: Boolean, byte: Byte, short: Short, int: Int,
15 | long: Long, float: Float, double: Double, binary: Array[Byte], list: List[String],
16 | set: Set[String], obj: TestCC, map: Map[String, Int])
17 | object CompleteItem extends HasGenCodec[CompleteItem]
18 |
--------------------------------------------------------------------------------
/utils/.js/src/main/scala/io/udash/utils/CrossCollections.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package utils
3 |
4 | import com.avsystem.commons._
5 |
6 | import scala.scalajs.js
7 |
8 | object CrossCollections {
9 |
10 | import scala.scalajs.js.JSConverters._
11 |
12 | def toCrossArray[T](t: Iterable[T]): MBuffer[T] = t.toJSArray
13 | def createArray[T]: MBuffer[T] = js.Array[T]()
14 | def createDictionary[T]: MMap[String, T] = js.Dictionary[T]()
15 | def createMap[K, V]: MMap[K, V] = js.Map.empty[K, V]
16 | def createSet[T]: MSet[T] = js.Set.empty[T]
17 | def copyArray[T](a: MBuffer[T]): MBuffer[T] = a.toJSArray.jsSlice()
18 | def slice[T](a: MBuffer[T], from: Int, to: Int): MBuffer[T] = a.toJSArray.jsSlice(from, to)
19 | def replace[T](a: MBuffer[T], idx: Int, count: Int, items: T*): Unit = replaceSeq(a, idx, count, items)
20 | def replaceSeq[T](a: MBuffer[T], idx: Int, count: Int, items: BSeq[T]): Unit =
21 | a.toJSArray.splice(idx, count, items.toSeq: _*)
22 | }
23 |
--------------------------------------------------------------------------------
/utils/.js/src/main/scala/io/udash/utils/URLEncoder.scala:
--------------------------------------------------------------------------------
1 | package io.udash.utils
2 |
3 | import scala.scalajs.js
4 |
5 | object URLEncoder {
6 | def encode(query: String, spaceAsPlus: Boolean): String = {
7 | val res = js.URIUtils.encodeURIComponent(query)
8 | .replace("!", "%21")
9 | .replace("'", "%27")
10 | .replace("(", "%28")
11 | .replace(")", "%29")
12 | .replace("~", "%7E")
13 |
14 | if (spaceAsPlus) res.replace("%20", "+") else res
15 | }
16 |
17 | def decode(query: String, plusAsSpace: Boolean): String = {
18 | val pre = if (plusAsSpace) query.replace("+", "%20") else query
19 | js.URIUtils.decodeURIComponent(pre)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/utils/.js/src/test/scala/io/udash/testing/UdashFrontendTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash.testing
2 |
3 | import com.avsystem.commons._
4 | import org.scalajs.dom
5 | import org.scalajs.dom.{DOMTokenList, Element}
6 | import org.scalatest.enablers.Containing
7 |
8 | trait FrontendTestUtils {
9 | def emptyComponent(): Element = dom.document.createElement("div")
10 |
11 | implicit val DOMTokensListContains: Containing[DOMTokenList] = new Containing[DOMTokenList] {
12 | override def contains(container: DOMTokenList, element: Any): Boolean = element match {
13 | case s: String => container.contains(s)
14 | case _ => false
15 | }
16 |
17 | override def containsOneOf(container: DOMTokenList, elements: BSeq[Any]): Boolean = elements.exists {
18 | case s: String => container.contains(s)
19 | case _ => false
20 | }
21 |
22 | override def containsNoneOf(container: DOMTokenList, elements: BSeq[Any]): Boolean = elements.forall {
23 | case s: String => container.contains(s)
24 | case _ => false
25 | }
26 | }
27 | }
28 |
29 | trait UdashFrontendTest extends UdashSharedTest with FrontendTestUtils
30 |
31 | trait AsyncUdashFrontendTest extends AsyncUdashSharedTest with FrontendTestUtils
--------------------------------------------------------------------------------
/utils/.jvm/src/main/scala/io/udash/logging/UdashLogger.scala:
--------------------------------------------------------------------------------
1 | package io.udash.logging
2 |
3 | import com.typesafe.scalalogging.Logger
4 | import org.slf4j.LoggerFactory
5 |
6 | class UdashLogger(clazz: Class[_]) extends CrossLogger {
7 | private val internalLogger = Logger(LoggerFactory.getLogger(clazz.getName))
8 |
9 | override def debug(message: String, params: Any*): Unit =
10 | internalLogger.debug(message, params)
11 |
12 | override def debug(message: String, cause: Throwable): Unit =
13 | internalLogger.debug(message, cause)
14 |
15 | override def info(message: String, params: Any*): Unit =
16 | internalLogger.info(message, params)
17 |
18 | override def info(message: String, cause: Throwable): Unit =
19 | internalLogger.info(message, cause)
20 |
21 | override def warn(message: String, params: Any*): Unit =
22 | internalLogger.warn(message, params)
23 |
24 | override def warn(message: String, cause: Throwable): Unit =
25 | internalLogger.warn(message, cause)
26 |
27 | override def error(message: String, params: Any*): Unit =
28 | internalLogger.error(message, params)
29 |
30 | override def error(message: String, cause: Throwable): Unit =
31 | internalLogger.error(message, cause)
32 | }
33 |
--------------------------------------------------------------------------------
/utils/.jvm/src/main/scala/io/udash/utils/CrossCollections.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package utils
3 |
4 | import com.avsystem.commons._
5 |
6 | object CrossCollections {
7 | def toCrossArray[T](t: Iterable[T]): MBuffer[T] = t.to(MArrayBuffer)
8 | def createArray[T]: MBuffer[T] = new MArrayBuffer[T]
9 | def createDictionary[T]: MMap[String, T] = new MHashMap[String, T]
10 | def createMap[K, V]: MMap[K, V] = new MHashMap[K, V]
11 | def createSet[T]: MSet[T] = new MHashSet[T]
12 | def copyArray[T](a: MBuffer[T]): MBuffer[T] = a.to(MArrayBuffer) // creates copy
13 | def slice[T](a: MBuffer[T], from: Int, to: Int): MBuffer[T] = a.slice(from, to)
14 | def replace[T](a: MBuffer[T], idx: Int, count: Int, items: T*): Unit =
15 | replaceSeq(a, idx, count, items)
16 | def replaceSeq[T](a: MBuffer[T], idx: Int, count: Int, items: BSeq[T]): Unit = {
17 | a.remove(idx, count)
18 | a.insertAll(idx, items)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/utils/.jvm/src/main/scala/io/udash/utils/URLEncoder.scala:
--------------------------------------------------------------------------------
1 | package io.udash.utils
2 |
3 | object URLEncoder {
4 | def encode(query: String, spaceAsPlus: Boolean): String = {
5 | val res = java.net.URLEncoder.encode(query, "UTF-8")
6 | if (spaceAsPlus) res else res.replace("+", "%20")
7 | }
8 |
9 | def decode(query: String, plusAsSpace: Boolean): String = {
10 | val pre = if (plusAsSpace) query else query.replace("+", "%2B")
11 | java.net.URLDecoder.decode(pre, "UTF-8")
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/utils/.jvm/src/test/scala/io/udash/testing/AsyncUdashSharedTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash.testing
2 |
3 | import org.scalactic.source.Position
4 | import org.scalatest.concurrent.Eventually
5 | import org.scalatest.concurrent.PatienceConfiguration.{Interval, Timeout}
6 | import org.scalatest.{Assertion, Succeeded}
7 |
8 | import scala.concurrent.{ExecutionContext, Future}
9 |
10 | trait AsyncUdashSharedTest extends AsyncUdashSharedTestBase with Eventually {
11 | override implicit def executionContext: ExecutionContext = ExecutionContext.global
12 |
13 | override def retrying(code: => Any)(implicit patienceConfig: PatienceConfig, pos: Position): Future[Assertion] = {
14 | Future {
15 | eventually(Timeout(patienceConfig.timeout), Interval(patienceConfig.interval))(code)
16 | Succeeded
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/utils/src/main/scala/io/udash/logging/CrossLogger.scala:
--------------------------------------------------------------------------------
1 | package io.udash.logging
2 |
3 | trait CrossLogger {
4 | def debug(message: String, params: Any*): Unit
5 | def debug(message: String, cause: Throwable): Unit
6 |
7 | def info(message: String, params: Any*): Unit
8 | def info(message: String, cause: Throwable): Unit
9 |
10 | def warn(message: String, params: Any*): Unit
11 | def warn(message: String, cause: Throwable): Unit
12 |
13 | def error(message: String, params: Any*): Unit
14 | def error(message: String, cause: Throwable): Unit
15 | }
16 |
--------------------------------------------------------------------------------
/utils/src/main/scala/io/udash/logging/CrossLogging.scala:
--------------------------------------------------------------------------------
1 | package io.udash.logging
2 |
3 | trait CrossLogging {
4 | protected def logger: CrossLogger = new UdashLogger(this.getClass)
5 | }
6 |
--------------------------------------------------------------------------------
/utils/src/main/scala/io/udash/utils/FilteringUtils.scala:
--------------------------------------------------------------------------------
1 | package io.udash.utils
2 |
3 | import com.avsystem.commons._
4 |
5 | object FilteringUtils {
6 | /** Finds the longest prefix with equal elements. */
7 | def findEqPrefix[T](newPath: Iterator[T], previousPath: Iterator[T]): Iterator[T] =
8 | newPath.zip(previousPath).takeWhile { case (h1, h2) => h1 == h2 }.map(_._1)
9 |
10 | /** Finds @newPath suffix which is different than in @previousPath. */
11 | def findDiffSuffix[T](newPath: Iterator[T], previousPath: Iterator[T]): Iterator[T] =
12 | newPath.map(_.opt).zipAll(previousPath.map(_.opt), Opt.Empty, Opt.Empty).dropWhile { case (h1, h2) =>
13 | h1 == h2
14 | }.flatMap(_._1)
15 | }
16 |
--------------------------------------------------------------------------------
/utils/src/main/scala/io/udash/utils/Registration.scala:
--------------------------------------------------------------------------------
1 | package io.udash.utils
2 |
3 | import scala.collection.mutable
4 |
5 | /** Should be returned from every callback registration method in Udash. */
6 | trait Registration {
7 | /** Removes registered callback */
8 | def cancel(): Unit
9 |
10 | /** Registers callback again. */
11 | def restart(): Unit
12 |
13 | /** Returns `true`, if callback is active. */
14 | def isActive: Boolean
15 | }
16 |
17 | private[udash] class SetRegistration[ElementType](s: mutable.Set[ElementType], el: ElementType) extends Registration {
18 | override def cancel(): Unit = s.synchronized { s -= el }
19 | override def restart(): Unit = s.synchronized { s += el }
20 | override def isActive: Boolean = s.synchronized { s.contains(el) }
21 | }
--------------------------------------------------------------------------------
/utils/src/test/scala/io/udash/testing/AsyncUdashSharedTestBase.scala:
--------------------------------------------------------------------------------
1 | package io.udash.testing
2 |
3 | import org.scalactic.source.Position
4 | import org.scalatest.{Assertion, BeforeAndAfterAll}
5 | import org.scalatest.concurrent.PatienceConfiguration
6 | import org.scalatest.time.{Millis, Span}
7 |
8 | import scala.concurrent.Future
9 | import org.scalatest.matchers.should.Matchers
10 | import org.scalatest.wordspec.AsyncWordSpec
11 |
12 | trait AsyncUdashSharedTestBase extends AsyncWordSpec with Matchers with BeforeAndAfterAll with PatienceConfiguration {
13 | case class RetryingTimeout() extends Exception
14 |
15 | override implicit val patienceConfig: PatienceConfig = PatienceConfig(scaled(Span(5000, Millis)), scaled(Span(100, Millis)))
16 |
17 | def retrying(code: => Any)(implicit patienceConfig: PatienceConfig, pos: Position): Future[Assertion]
18 | }
19 |
--------------------------------------------------------------------------------
/utils/src/test/scala/io/udash/testing/CompilationErrorAssertions.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package testing
3 |
4 | import org.scalatest.Assertions
5 |
6 | trait CompilationErrorAssertions extends Assertions {
7 | def typeErrorFor(code: String): String = macro io.udash.macros.TestMacros.typeErrorImpl
8 | }
9 |
--------------------------------------------------------------------------------
/utils/src/test/scala/io/udash/testing/UdashSharedTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash.testing
2 |
3 | import org.scalatest.BeforeAndAfterAll
4 | import org.scalatest.matchers.should.Matchers
5 | import org.scalatest.wordspec.AnyWordSpec
6 |
7 | trait UdashSharedTest extends AnyWordSpec with Matchers with BeforeAndAfterAll {
8 | }
9 |
--------------------------------------------------------------------------------
/utils/src/test/scala/io/udash/utils/URLEncoderTest.scala:
--------------------------------------------------------------------------------
1 | package io.udash
2 | package utils
3 |
4 | import io.udash.testing.UdashSharedTest
5 |
6 | class URLEncoderTest extends UdashSharedTest {
7 |
8 | "URLEncoder" should {
9 | "encode and decode data in the same way for both JVM and JS" in {
10 | val data = "a b »~!@#$%^&*()_+=-`{}[]:\";'<>?/.,♦"
11 |
12 | URLEncoder.encode(data, spaceAsPlus = true) should
13 | be("a+b+%C2%BB%7E%21%40%23%24%25%5E%26*%28%29_%2B%3D-%60%7B%7D%5B%5D%3A%22%3B%27%3C%3E%3F%2F.%2C%E2%99%A6")
14 | URLEncoder.decode(URLEncoder.encode(data, spaceAsPlus = true), plusAsSpace = true) should be(data)
15 |
16 | URLEncoder.encode(data, spaceAsPlus = false) should
17 | be("a%20b%20%C2%BB%7E%21%40%23%24%25%5E%26*%28%29_%2B%3D-%60%7B%7D%5B%5D%3A%22%3B%27%3C%3E%3F%2F.%2C%E2%99%A6")
18 | URLEncoder.decode(URLEncoder.encode(data, spaceAsPlus = false), plusAsSpace = false) should be(data)
19 | }
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------