Skip to content

Latest commit

 

History

History
832 lines (755 loc) · 35.3 KB

journal.org

File metadata and controls

832 lines (755 loc) · 35.3 KB

Readme

with tasks filter: https://www.frontendmentor.io/challenges?sort=difficulty|asc&difficulties=2&types=free-plus,free&hideCompleted=true

site with projects to practice front end dev, let’s try this out

deploying into cloud flare

https://dash.cloudflare.com

dev thigngs

file:///home/enefedov/Documents/personal/frontendmentor/ link to open designs in firefox

getting svg

https://laminar.dev/documentation#parsing-html-or-svg-strings-into-elements

11:58 debug to show all boxes \ outline of div elements:

https://stackoverflow.com/questions/38591246/is-there-a-way-to-see-the-borders-around-all-div-tags-on-a-web-page/38591324#38591324

div {
    outline: 1px solid red;
}

starting a second task:

https://www.frontendmentor.io/challenges/product-preview-card-component-GO7UmttRfa/hub

and picking the colors!

enefedov@enefedov-ubuntu:~$ nix run nixpkgs#gpick

exercise 2. product card

task 3 - question & interacting

https://www.frontendmentor.io/challenges/interactive-rating-component-koxpeBUmI https://www.frontendmentor.io/challenges/interactive-rating-component-koxpeBUmI/hub

here’s snippet for attribution

def renderAttribution(): Element = {
  footerTag(
    role := "contentinfo",
    className := "absolute inset-x-0 bottom-2 attribution",
    "Challenge by ",
    a(
      href := "https://www.frontendmentor.io?ref=challenge",
      target := "_blank",
      "Frontend Mentor"
    ),
    " Coded by ",
    a(href := "#", "Your Name Here")
  )
}

measuring parts of page - the sizes of elements inside of page or image:

https://firefox-source-docs.mozilla.org/devtools-user/measure_a_portion_of_the_page/index.html

DevTools > Settings > Available tools > Meausre part of page

then ruler appears in the dev tools, yay

setting page font base size

html {
  font-size: 14px;
}

color tools

https://www.rapidtables.com/convert/color/hsl-to-rgb.html enefedov@enefedov-ubuntu:~$ nix run nixpkgs#gpick

here’s task 3 solution:

https://www.frontendmentor.io/solutions/responsive-by-tailwindcss-on-vite-with-scalajs-and-laminar-W2wOVlyo6d

let’s fix first 3 tasks. add Landmarks and alternate text to images

adding accessibility features:

big level split

https://dequeuniversity.com/rules/axe/4.6/landmark-one-main?application=axeAPI https://dequeuniversity.com/rules/axe/4.6/page-has-heading-one?application=axeAPI <header role=”banner”> <nav role=”navigation”> <main role=”main”> <footer role=”contentinfo”>

headers support:

https://dequeuniversity.com/rules/axe/4.6/page-has-heading-one?application=axeAPI use at least one <h1> to mark beginning of content

using rustywind for my scala files:

and put rustywind binary into /usr/bin

(defun my/rustywind-in-project-root ()
  (interactive)
  (let ((command (mapconcat 'identity
                            (list "rustywind ."
                                  "--custom-regex"
                                  (shell-quote-argument "className := \"([^\"]+)\"")
                                  "--write") " ")))
    (projectile-run-shell-command-in-root command)
    (revert-buffer t t)))

(add-hook 'after-save-hook #'my/rustywind-in-project-root)
(remove-hook 'after-save-hook #'my/rustywind-in-project-root)

task 4, yay

https://www.frontendmentor.io/challenges/age-calculator-app-dF9DFFpj-Q https://www.frontendmentor.io/challenges/age-calculator-app-dF9DFFpj-Q/hub

starting with it. copying the initted directory, changing names, and unpacking resources.

product-preview-card-component

now for task 4, how do i arrange subcomponents?

they all should share the Date object, i suppose three inputs would be in a single component? they would have their own validation, maybe defined in the top component, where there’s Date and what? how do i connect them to the single model? there’s a way surely

using java.security.UUID in scalajs requires additionad dependency:

https://github.com/scala-js/scala-js/security/advisories/GHSA-j2f9-w8wh-9ww4 Patches

Scala.js v1.10.0 fixes the issue. It uses java.security.SecureRandom to implement randomUUID().

java.security.SecureRandom is not provided by Scala.js core. Therefore, to be able to use randomUUID(), you will need to add a dependency on scalajs-java-securerandom. Failing to do so will result in linking errors (i.e., fastLinkJS/fullLinkJS will fail).

ok, so, i also need to reset date to None when there’s error, yuk

libraryDependencies += (“org.scala-js” %%% “scalajs-java-securerandom” % “1.0.0”).cross(CrossVersion.for3Use2_13)

OMG firefox image vertical alignment

There’s no built-in way in Firefox (or any browser) to adjust the positioning of an image opened directly in a browser tab. However, you can use a simple JavaScript hack in the browser console to accomplish this:

Open your browser’s console by pressing F12 or Ctrl+Shift+K on your keyboard.

In the console, paste the following command and hit Enter:

document.querySelector('img').style.marginTop = "50px";

Replace “50px” with whatever amount of spacing you’d like. This command will add a top margin to the image, pushing it down the page. However, please note that this change is temporary and will not persist when the page is refreshed. If you want a permanent solution, you would need to use CSS in the webpage’s code to adjust the image’s positioning.

what i’ve learned about ValidatedElement

i can bind styles of others to it, but it doesn’t have “.amend”, so i could put “validatedElement.el” into the dom, and call “.amend” on it

starting task 5

https://www.frontendmentor.io/challenges/newsletter-signup-form-with-success-message-3FC1AZbNrv/hub yay

so, i need what? boolean for successful submission.

in real world can be filled after receiving 200 from backend.

and yay, to the even harder task 6

https://www.frontendmentor.io/challenges/interactive-comments-section-iG1RugEG9/hub

so. let’s look at readme and breakdown things to do?

reply button that opens form for submitting new comment

with text field and button to submit Reply

own comments - no reply, but edit and delete

label “you” on own comments

all sub-top-level comments are on the 2nd level

so, walk all? or, top level are comments and others are replies? ok, yeah, there are two different models. ok, ok. let’s change that.

buttons to change rating

bottom form to Send new comment

prefilling @username when replying

ok. how should that work though?

should have a component for the comment or reply.

maybe get the subcomponent that displays either, by the shared trait. and almost everything should be same?

i guess on the backend side it could be Cassandra with messages, and what? stored in same table, with optional fields

the Reply function would be different for Commend and Reply. or, not too different, but yeah

but that function can be initialized by prent container

the Reply window active state, can be hidden in the same Message Component, and opened when activating Reply.

ok? let’s start with writing a Message component,

it would be able to take in different action based on parent, whether it’s CommentReply or ReplyReply,

but lots of common logic:

display message, author, score

the position of Reply and Vote elements are totally different on desktop and mobile for the first time attempting to do with Grid, potentially overcomplicating?

CANCELLED where should voting information be stored?

to track what person already voted? i could disallow re-voting for now, or just store 2 lists of IDs, who upvoted and who downvoted and allow re-voting

functionality for reply (on others)

as separate component, ok

checking and displaying if OWN

functionality to delete - create modal, and also call function from parent?

https://blog.webdevsimp lified.com/2023-04/html-dialog/ let’s try to do the dialog

done for top level without modal

how do i do this with modal?

functionality to edit - swap body with form

yeah. let’s just do a bit somewhat copy? it would also always be for own

make hover on buttons - making them more white

done in ugly and partial way. inlining svg would allow it to be styled with css? with "text-blue-500 fill-current"?

CANCELLED add @tag to the replies display.

you know what! I will not be doing this! the design also wants me to bug ‘at-tag’ into response text field, and what am i to do with them? are they editable? this is not what I want

CANCELLED remove “voting” for own messages

save state into local storage

let’s do it with upickle and json

component to reply \ send message could be shared?

i guess yeah, passing in function on what to submit and where, and it could be reused.

component for Whole Comment with Replies

component for all comments

component for whole comment section & submit new top level ui

text wrap, when word is longer than element width

submit by ctrl + Enter

let’s style the desktop version

implement relative time

for some other time:

implement ‘at-tag’s

display in the message body as links,

maybe even go to the place in the page for the thing that got replied to it can be both Comment and Reply

put them into edit box. somehow

and what? are they supposed to be editable?

inline svg, check if they can be color-styled

[#A] fix accessibility report errors

yay, just another minute of searching, and there’s function to translate colors

using java.time in ScalaJS :

need dependency https://github.com/cquiroz/scala-java-time libr

aryDependencies += "io.github.cquiroz" %%% "scala-java-time" % "2.5.0",

new exciting error:

[error] – Error: /home/enefedov/Documents/personal/frontendmentor/6-interactive-comment-section/src/main/scala/industries/sunshine/interactivecommentsection/CommentComponent.scala:25:35 [error] 25 | stateVar.zoom(.comments.eachWhere(.message.id == uid))((state, newMessage) => { [error] | ^^^^^^^^^ [error] |Reference to method eachWhere in package com.softwaremill.quicklens should not have survived, [error] |it should have been processed and eliminated during expansion of an enclosing macro or term erasure.

this was because zoom is probably also a macros, and also should just have a getter as first argument

also, would be better to just split into mapped signal and function that does lense update in the parent

rewrite message component to 2 functions

rewrite comment

usage of dialog in the Laminar:

val deletionDialog = dialogTag(
  className := "backdrop:bg-black/50",
  form(
    method := "dialog",
    p("Are you sure?"),
    button(
      `type` := "submit", // html logic, will close modal, and not submit form
      onClick --> Observer(_ => onDelete()),
      onClick --> Observer(_ => println("submittign form")),
      "YES, DELETE"
    ),
  )
)

div(
  className := "flex flex-row items-center",
  deletionDialog, // put dialog into dom
  button(
    className := "flex flex-row items-center mr-7 text-sm font-bold text-soft-red",
    onClick --> Observer(_ => deletionDialog.ref.showModal()), // acces dom ref, it's already typed. wow
    "Delete"
  ),
)

new syntax for bg opacity:

className := "backdrop:bg-light-gray/75",

https://tailwindcss.com/docs/background-color#changing-the-opacity

problem: dialog doesn’t close on Escape for some reason

local storage

https://www.scala-js.org/api/scalajs-dom/0.6/index.html#org.scalajs.dom.WindowLocalStorage

first big interactions with onMount* things

onMountInsert onMountCallback

also ctx.thisNode.ref.focus() access

also html “autofocus” attribute, which is not what i want, or, yeah. exactly what i want! no, not what I want - when the page loads.

attempted visibility

visibility <-- messageSignal.map(msg => if (msg.user != selfUser) "visible" else "collapse"),

but actually in design, you should be able to vote for yourself

installed webcomponent for relative time, now want a custom tag

https://github.com/github/relative-time-element

npm install @github/relative-time-element

what i want from tag:

<relative-time datetime="2014-04-01T16:30:00-08:00">
  April 1, 2014
</relative-time>

just name and datetime attribute

i created tag and attribute:

val relativeTimeTag = htmlTag("relative-time")
val relTimeDatetime = htmlAttr("datetime", StringAsIsCodec)

and then used it:

div(
className := "pl-3 text-light-gray",
child <-- messageSignal.map(message => {
  val scalaInstant = message.createdAt
  val isoTime =
    java.time.Instant.ofEpochSecond(scalaInstant.getEpochSecond())
  relativeTimeTag(
    scalaInstant.toString().take(10),
    relTimeDatetime := isoTime.toString()
  )
})

and for some reason i needed:

<!– TODO i have no idea why code doesn’t work without it – – in the console i see ‘already-registered’ –> <script type=”module”> import RelativeTimeElement from ‘/node_modules/@github/relative-time-element/dist/index.js’; if (!window.customElements.get(‘relative-time’)) { window.customElements.define(‘relative-time’, RelativeTimeElement); } else { console.log(“already-registered”) } </script>

submitting: Grid, Tailwind, Webcomponent in ScalaJS & Laminar with Vite

Now, this was an increase in complexity!

Few things are undone:

  1. Not fully understanding reasons for needing “registration” a relative-time webcomponent in my `index.html` - specifically because log shows “already registered”, so code does nothing? But without it things don’t work.
  2. Changing color of SVG elements, maybe it’s easy if I inline them as code
  3. Not adding ‘@’ tag to replies. current component hierarcy makes it awkward to conditionally display ‘@’ in replies and not in top level comments, also - in the “edit” mode, design docs are not detailed enough to explain presence of ‘@user’ - is it editable? what happens when it’s edited?

Note: code would be a lot simpler in a full-stack app. With laminar I can propagate Stream of app state to all components, from the server websocket that sends all updates, so no callbacks to register state chages from the child components would be necessary, only doing http requests to api from the child components.

Things done:

  1. Message component
    • display message, author, score
    • functionality for reply
    • checking and displaying if OWN
    • functionality to delete - create modal https://blog.webdevsimplified.com/2023-04/html-dialog/
    • functionality to edit - swap body with form
    • make hover on buttons - making them more white
  2. save state into local storage with easy serialization into JSON
  3. component to reply \ send message could be shared?
  4. component for Whole Comment with Replies
  5. component for all comments
  6. component for whole comment section & submit new top level ui
  7. text wrap, when word is longer than element width
  8. submit by ctrl + Enter
  9. let’s style the desktop version
  10. add relative time webcomponent

starting task 7

https://www.frontendmentor.io/challenges/news-homepage-H6SWTa1MFl/hub and I’m downgrading to Junior i guess this is more about html and css

well, i guess i can use StoryCard class

with title, description, and image link. and in the New use that same, model, just don’t reference image

for design, i’d need some planning.

there’s almost zero coding logic, but layout needs to be thought out.

mobile has button that opens menu, sliding from the right, could be modal?

still ok to implement as separate div component with links, and then wrap in modal

header -

on moble with menu button, on desktop directly with links. i guess could have 2 components, and then wrapped into logic of showing one or another

the sections seem easy after 70% of Wes Bos course:

from 3 columns, goes to 1 column on mobile. i could have named areas, but i guess just direct order is good enough? big elements seem to always be 3 rowh in height and small stories can just be directly in the grid with 1fr height and 1fr width

i’ll need separate components for:

main story & picture, can be Grid

small story & picture, can be Gric

the New column, can just be in same file as ‘new story’ component

header

mobile menu

overall page grid

vertical

maybe 170 per row and 60 gap?

deployed

https://efim-frontendmentor-news-homepage.pages.dev/

readme and submit, yay

adding downloaded fonts to TailwindCSS

import them in style.css

@font-face {
  font-family: 'Inter';
  src: url('/assets/fonts/static/Inter-Regular.ttf') format('truetype');
  font-weight: 400; /* Regular */
  font-style: normal;
}

@font-face {
  font-family: 'Inter';
  src: url('/assets/fonts/static/Inter-Bold.ttf') format('truetype');
  font-weight: 700; /* Bold */
  font-style: normal;
}

@font-face {
  font-family: 'Inter';
  src: url('/assets/fonts/static/Inter-ExtraBold.ttf') format('truetype');
  font-weight: 800; /* ExtraBold */
  font-style: normal;
}

register in tailwind theme:

/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./index.html",
    "./src/**/*.scala",
  ],
  theme: {
    extend: {
      fontFamily: {
        'inter': ['Inter', 'sans-serif'],
      },
    },
  },
  plugins: [],
}

now it should be available to set:

div(
  className := "font-inter",
  " Home "
)

ok, i found out that I can just add hsl colors into Tailwind config =)

with modal mobile menu, lot’s of help from chat gpt, so there are lots of code? articles about laminar?

or maybe it reads articles about doing this with js and translates this logic into laminar?

found out about “fixed” positioning - other content scrolls intependently. wowy

time to ask chatgpt to improve my tailwind classes sorting

current way.

attempting to give feedback to a fellow student:

https://www.frontendmentor.io/solutions/solution-using-html-and-css-with-flex-and-grid-HaxK3b5tEb

reply

Hi! I’m not a senior in frontend dev, but wanted to try to figure out your question: “In the mobile view, the text has moved together a bit, what could be the reason or how can this be fixed?”

What I did is opened your website in Firefox, entered mobile view with “Ctrl + Shift + M” (or Menu -> More Tools -> Mobile Design Mode) and then opened dev tools with F12.

The “context menu” (right click) on the text and then “inspect element” highlight that element in the dom in the menu, and also allow us to see sized of “body \ padding \ margin”

I’m putting the screenshots in my repo and will paste links:

  1. this is me inspecting the element in question: https://github.com/efim/frontendmentor-exercises/blob/master/feedback/1/looking-for-padding-reason-2023-06-06_18-59.png you can see that putting cursor at the blue inner rectangle (1) - the body gets highlighted at the page (2), but no padding or margin is detected
  2. then I check elements higher in the hierarchy, and finding this one https://github.com/efim/frontendmentor-exercises/blob/master/feedback/1/found-padding-2023-06-06_18-58.png setting cursor to (1) purple “padding” place - highlights the area of your webpage that padding takes (2) so, this is the element that results in text getting smushed
  3. then i can also temporarily change the style right in the browser dev tools: https://github.com/efim/frontendmentor-exercises/blob/master/feedback/1/changing-manually-2023-06-06_19-00.png setting left and right to 1rem removes this smushing

You’ll probably want to have media queries for mobile size that set different paddings for the element. and maybe even not using paddings to make blue background take whole width, but just make it’s width 100% or something. Here unfortunately I can’t give specific advice, because I’ve started almost immediately with TailwindCSS and it’s a bit different from css

Hope this helps!

reading about doing drag-and-drop sorting with webcomponents.

first into sap ui5

https://sap.github.io/ui5-webcomponents/playground/components and their bindings for laminar: https://index.scala-lang.org/sherpal/laminarsapui5bindings examples : https://sherpal.github.io/laminar-ui5-demo/?componentName=

then into material ui

https://github.com/uosis/laminar-web-components https://laminar.dev/examples/web-components

it seems that “draggable things” are not generally implemented on level of webcomponents.

was recommended to look into “Dragula” or “SortableJS” libraries, which should be somehow used from ScalaJS

there’s also Drag and Drop api in HTML 5, but it seems low level, the elements could emit “dragstart”, or “dragover”, or “dragend” events, but i’d have to implement handlers for updating indices in the list (easy part), and showing element while it’s dragged (should i? hard part)

maybe i’d need to revisit guide on integration with js libraries:

https://www.scala-js.org/doc/tutorial/scalablytyped.html

i do want to pick up the exercise where i’d have to figure this part out:

https://www.frontendmentor.io/challenges/todo-app-Su1_KokOW

task 8 - countdown clock

styling clock, the cutout between the flippers is a half circle, not just rounded corners,

but let’s skip this part for now

acquiring time zone:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat

adding timezones to java.time in ScalaJS:

http://cquiroz.github.io/scala-java-time/

libraryDependencies += “io.github.cquiroz” %%% “scala-java-time-tzdb” % “2.0.0-RC3_2019a”

now let’s add settings?

ability to set the target date. and also in that menu let’s show also updating millis, just to get proof that thing is fast at this

ok. yeah. ugh.

well, i did the desktop design. now i want to settings menu.

do i want to allow to change “local” time zone?

searching for free icons:

https://www.svgrepo.com/svg/509956/gear?edit=true

attempting to add modal,

it’s opening, but for some reason not closing. dialog and modal in Laminar ScalaJS why would that be?

that was becasue i was “preventDefault” on the submit button. cool

Submitting 8

Well, the TailwindCSS styling took me much longer than I anticipated. I was expecting a very quick task, instead the background was not trivial, the round cutouts in the clock element - much more complicated that i thought, not to say anything about the animation.

I was successful though in displaying countdown, and very happy with the speed of changing display - with milliseconds in the optional settings menu.

Have a few things to try out in ScalaJS, and then potentially get a pause in use of that nice technology, which still feels a big clunky when I try to reuse existing JS libraries, whithout which there’s too much to be created anew.

ok, starting 9. the ip addr tracker.

will do first map?

and only then api calls? because there would be only 500 total for account? not per month?

https://geo.ipify.org/docs

example request:

https://geo.ipify.org/api/v2/country,city?apiKey=YOUR_API_KEY&ipAddress=8.8.8.8 GET

ipAddress
Optional. IPv4 or IPv6 to search location by. If the parameter is not specified, then it defaults to client request’s public IP address.
domain
Optional. Domain name to search location by. If the parameter is not specified, then ‘ipAddress’ will be used.

example response:

{
    "ip": "8.8.8.8",
    "location": {
        "country": "US",
        "region": "California",
        "city": "Mountain View",
        "lat": 37.40599,
        "lng": -122.078514,
        "postalCode": "94043",
        "timezone": "-07:00",
        "geonameId": 5375481
    },
    "domains": [
        "0d2.net",
        "003725.com",
        "0f6.b0094c.cn",
        "007515.com",
        "0guhi.jocose.cn"
    ],
    "as": {
        "asn": 15169,
        "name": "Google LLC",
        "route": "8.8.8.0/24",
        "domain": "https://about.google/intl/en/",
        "type": "Content"
    },
    "isp": "Google LLC"
}

and what are requirements for the map?

ok, i guess.

input would be populated with own ip.

well. first opening - i guess have empty state, and a loader on top of all

and then what? i guess app can have a state with case class of all info. and when it’s None - show loader. and start some kind of action when first visiting, tied to setter of state. and same logic could be used on a button.

and i guess i’d want the input would be form, and ip address validation. okok.

then i’ll have the sample json response, would want decoder into state.

could write a funtion that takes hardcode and puts into state.

and only as last step - register for api and do an actual call.

quesion: how do i put map on a lover level?

do i create a component with position ‘fixed’ and set it up from the parent component? so that what? function to set map coords could be exposed?

so low level component with bg, which is dynamicly selected from window size.

and map div, let’s keep it green of specific size get stream of coords. and what would it show on empty coords? whatever. would be nice to have animation

so, is that just single component? seems simple enought to be one file.

component with form : input and submit button

gets setter for the state

component that draws the table for data, when empty shows the loading thingy and overlay i guess

connected to state, which is on top level gets stream of state.

now create state class, Var in top level

well, i’ll want to tailor class to the sample json? would that be easy to do with upickle? let’s just use deriving and cover full json?

adding map to lover component, returning function to set map center and point with coords.

yeah. ok, not exactly, but let’s go on with this.

where do i first launch state initialization?

i suppose at the level that defines state.

using HTML5 input validation

<input
  type="text"
  required <!-- don't allow empty field -->
  pattern="^((\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$"
  <!-- check value against regex on submission -->
  title := "IPv4 address, like 192.168.0.1"
  <!-- this is added to validation error message after 'Please match the expected format:' -->
  >
</input>

this is already really cool!
can't do validation against the database, or from a combination of fields,
but seems good enough for many use cases. also there are many custom types, like email and whatnot.

with Laminar

with laminar small complication: html5 checks are done on default behavior, so if i’m disabling it (to prevent page reload), i need to call the logic on the elements:

form(
  className := "flex flex-row",
  input(
    className := "px-5 rounded-l-xl grow",
    placeholder <-- Utils.isMobileWidthStream.map(
      if (_) placeholderTextSmall else placeholderText
    ),
    typ := "text",
    required := true,
    pattern := """^((\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$""",
    title := "IPv4 address, like 192.168.0.1",
    controlled(
      value <-- ipInput,
      onInput.mapToValue --> ipInput
    )
  ),
  onMountInsert(ctx =>
    button(
      className := "grid place-content-center w-12 h-12 bg-black rounded-r-xl",
      typ := "submit",
      img(
        src := "/images/icon-arrow.svg",
        alt := "Start search"
      ),
      onClick.preventDefault.map(_ =>
        val validity = ctx.thisNode.ref.checkValidity()
        if (!validity) ctx.thisNode.ref.reportValidity()
        validity
      ) --> Observer(isFormValid => {
        println(s"submitted ${ipInput.now()} for form instate $isFormValid")
      })
    )
  )
)

So, “submitButton” is being created with access to form’s context, to allow easy usage of form’s ref and it’s functions:

  1. checkValidity :: boolean value if fields have passed validation
  2. reportValidity :: funciton that triggers invalid state and messages in invalid fields

I’m not sure programmatic validation of fields is easily integrated with this html5, if i only want it for some fields. for posteriority, here’s link to one way of validating forms in Laminar, with logic to change field states - https://laminar.dev/examples/form-state

so. what needs the state -

the inputs need setter

the info needs signal

the map needs coords signal

what’s next - let’s do map, i guess

setting up ScalaJS facade and installing leaflet

this is giude for setting up ScalablyTyped with externally managed npm: https://www.scala-js.org/doc/tutorial/scalablytyped.html

searching for verisons at https://www.npmjs.com/ ?

npm install leaflet
npm insall -D @types/leaflet typescript

here i got news about fresher version: https://scalablytyped.org/docs/plugin

and versions are same as in my previous experiment, so scala code is already generated, cool

now to set things up - the start guide from leaflet:

https://leafletjs.com/examples/quick-start/

how can i set css styles “after” loading of the library? if i’m bundling code? whelp, let’s add to index.html

now i need methods that would call api,

first hardcoding return, shared between initial setup, and form submittion and i could still keep the hardcoded initail star

https://laminext.dev/v/0.15.x/t

well, i get errors from ipify, complaints about my app key

there’s better, seemingly free, at certainly gratis

https://seeip.org/ but it’s backend only, because of CORS

and this one seems to work from front end:

https://ip-api.com/docs/api:json

huh, ipify worked,

but! for Tbilisi it didn’t have lat lng:

got FetchResponse(true,200,OK,[object Headers],cors,{“ip”:”176.221.162.56”,”location”:{“country”:”GE”,”region”:”K’alak’i T’bilisi”,”timezone”:”+04:00”},”as”:{“asn”:35805,”name”:”SILKNET-AS”,”route”:”176.221.128.0\/17”,”domain”:”http:\/\/www.silknet.com”,”type”:”Cable\/DSL\/ISP”},”isp”:”JSC "Silknet"”},https://geo.ipify.org/api/v2/country?apiKey=at_kxjQ1RWEj6dIsnWJwpOxHzuVlrPyT)

had to think about how to start initial Stream,

just put it on the left side of modifiers of ‘main’ cool.

desktop styling

remove default styling of the input field

yay, i’ve deployed

https://efim-frontendmentor-ip-tracker.pages.dev/

now let’s submit to frontendmentor:

Yes, I’ve not completed things fully:

  1. want to try free api - https://ip-api.com/docs/api:json it shouldn’t have “500 requests” limit, and my sample site would be available for longer
  2. use grid in the info panel
  3. write good readme

And add cool notes to this submittion. But! It works and I really want to cross it off my mind for a moment. ScalaJS, Laminar and integration with Leaflet maps through automatically binding that were automatically generated from Typescript interface by ScalablyTyped project, yay!

wow, cool example - exercise with lots of comments

https://github.com/Thewatcher13/product-card-perfume/tree/main

i found out about

picture tag, which can have different sources based on media query

s tag - to show inaccurate information

10. and now it’s time to do exercise with sorting.

https://www.frontendmentor.io/challenges/todo-app-Su1_KokOW/hub

i’ve kind of successfully set up sorting in example project. my guess is that facade is not generated correctly.

maybe would be cool to wrap unavailable things on my own. but OKOKOK, this is not quite good, but “good enough”.

is good enough actually a good thing here? i suppose that in actual production website it could be good enough, if ScalaJS had advantages in other areas.

let’s think about what’s needed:

again the background image and color,

i guess could be done as as in previous, with negative z level, here’s no map for interaction.

header with theme change button

image is changing with the theme change,

and icon on the change theme button and also tailwind classes, but i’ll read about that later

overall state with task model:

  1. list
  2. text
  3. isComplete

task creation needs update function

tasks list component with some group functions (and reordering things)

and counter of how many are left and “filter” buttons

one of the “filters” is active, by default “all”

so, what we put a filter into state i guess?

ok, how should i use filter. at the list level? it could be, yes. State at list level, with storing function in case classes yeah, and apply that to the incoming signal of list of element. yeah. ok.

i could put one of the objects as constructor argument to component.

and also signal of current state, and function to update state with own filter, and check of is active seems easy then, yay

single task display

with complete and delete buttons

now delete completed button

number of items left

desktop styling

drag and drop sorting

dark theme

https://tailwindcss.com/docs/dark-mode#toggling-dark-mode-manually

by default follows system theme, can be done programmatically via js setting class “dark” to some parent tag or have both using “matchMedia” api: https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia

and repo : https://github.com/efim/frontendmentor-exercises/tree/master/10-todo-list

and beck to working on active states

multiple attempts at making element visible on parent hover:

// className := "group-hover:bg-red-500 md:group-hover:visible",
// className := "md:hidden group-hover:bg-red-500 md:group-hover:visible",
// className := "visible hover:invisible", // so this works
className := "md:invisible md:group-hover:visible",

and let’s just ugh the active states on hover of the ‘clear all’ and ‘filters’

let’s just let it go for now.

trying to learn more about reactive images:

https://blog.webdevsimplified.com/2023-05/responsive-images/

picture tag would allow to have different sources

Responsive Images ! first time finding out about online resource with placeholder images:

https://placehold.co/

this seems maybe interesting. oh, for when i’m designing a gallery for example!

wow, thank you web dev simplified: https://blog.webdevsimplified.com/2023-05/responsive-images/ and it turns out there’s big article from Mozilla: https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images cool.

difference between <src> size set - it doesn’t downgrade image, it’s purpose is bandwidth optimization <picture> would change images in screen size increase and decrease - it’s purpose is selecting image for the media query size

very cool.

new things about “pointer events” - putting svg on top of input “checkbox”

https://tailwindcss.com/docs/pointer-events

exactly for putting icons on top of inputs, right?

let’s write readmes for tasks 9 and 10.

map and sorting. cool.