Isso parece fantástico IMHO.
Isso é simples de estender para coisas de terceiros, como Material-UI ?
Seria ótimo ter algum tipo de guia sobre como fazer isso. :)
Isso parece fantástico IMHO.
Obrigado! Que bom que gostou :smile:
Isso é simples de estender para coisas de terceiros, como Material-UI?
Claro, mas a abordagem é um pouco diferente porque não estamos usando sindicatos discriminados. Eu tento escrever algo quando o tempo permite, mas a ideia é que você tenha essencialmente duas opções:
prop
com mais funçõesprop
, específico para sua biblioteca (talvez Mui
) que tenha todas as propriedades que o Mui requer. Você pode até mesmo alias prop
com Mui
e estender Mui
com propriedades e funções específicas de Mui (sobrecarregadas).Estender é tão simples quanto:
type prop with
static member hello (value: string) = Interop.mkAttr "hello" value
Se esta é a "forma final" da biblioteca e a maneira de estendê-la, ainda está em consideração, pois primeiro quero experimentá-la construindo bibliotecas de terceiros e ver como funciona
Obrigado! Já estou bem adiantado testando algo para MUI, apenas para uma fração da API, para ver como funciona. Provavelmente compartilharei em algum momento durante a semana/fim, seria ótimo se você pudesse dar uma olhada só para ver se estou no caminho certo e seguindo o espírito Feliz. Parece sucesso até agora!
No entanto, não estendi o tipo prop
existente. Eu defini um tipo prop
separado em outro namespace ( Feliz.MaterialUI
). Funciona aparentemente muito bem; é claro que você terá acesso a todos os membros de todos os tipos correspondentes se abrir Feliz
e Feliz.MaterialUI
.
Eu tenho um tipo Mui
que corresponde a Html
e contém os componentes reais.
(Atualmente, coloquei props específicas de componentes em submódulos separados de prop
, conforme mencionado em #13.)
Um possível ponto de melhoria em Feliz é ter um reactElement
e createElement
que aceite não string
, mas ReactElementType
(eu acho). para que possamos chamar createElement (importDefault "@material-ui/core/Button")
. Eu mesmo criei esses dois ajudantes atualmente.
A propósito, todos os membros devem ser inline
? Quais são os prós/contras? Percebi que você não usou inline
acima, mas tudo em Feliz é inline
.
Obrigado! Já estou bem adiantado testando algo para MUI, apenas para uma fração da API, para ver como funciona. Provavelmente compartilharei em algum momento durante a semana/fim, seria ótimo se você pudesse dar uma olhada só para ver se estou no caminho certo e seguindo o espírito Feliz. Parece sucesso até agora!
Fantástico! Eu definitivamente ficaria feliz em dar uma olhada se você
No entanto, não estendi o tipo de prop existente. Eu defini um tipo de prop separado em outro namespace (Feliz.MaterialUI). Funciona aparentemente muito bem; é claro que você terá acesso a todos os membros de todos os tipos correspondentes se abrir Feliz e Feliz.MaterialUI.
Eu tenho um tipo Mui que corresponde a Html e contém os componentes reais.
Isso é o que eu faria por Mui
(Atualmente, coloquei props específicas de componentes em submódulos separados de prop, conforme mencionado em #13.)
Faz sentido para Mui por causa de quantas opções você tem
Um possível ponto de melhoria no Feliz é ter um reactElement e createElement que não aceite string, mas ReactElementType (eu acho). para que possamos chamar createElement (importDefault "@material-ui/core/Button"). Eu mesmo criei esses dois ajudantes atualmente.
Estou revisando os membros que estou usando no módulo Interop
, acabei de expor o que estava usando na biblioteca, será reconsiderado para a versão estável
A propósito, todos os membros devem estar em linha? Quais são os prós/contras? Percebi que você não usou inline acima, mas tudo em Feliz é inline.
Eu deveria ter alinhado o membro estendido acima!
A regra geral é: se o valor da propriedade é primitivo como uma string/int/bool/enum então inline a propriedade, mas se sua propriedade calcula um valor com base na entrada, então é melhor não inline porque toda vez que o usuário chama a função embutida, todo o corpo da função é embutido nesse local de invocação, portanto, se um usuário usar a mesma função 10 vezes em todo o aplicativo, o corpo da função será compilado em linha 10 vezes em vez de ser definido uma vez e referenciado 10 vezes
A regra geral é: se o valor da propriedade é primitivo como uma string/int/bool/enum então inline a propriedade, mas se sua propriedade calcula um valor com base na entrada, então é melhor não inline porque toda vez que o usuário chama a função embutida, todo o corpo da função é embutido nesse local de invocação, portanto, se um usuário usar a mesma função 10 vezes em todo o aplicativo, o corpo da função será compilado em linha 10 vezes em vez de ser definido uma vez e referenciado 10 vezes
Bom saber! Mas (no contexto de Fable) por que inline em primeiro lugar, então? Qual é o benefício para os corpos de função/método "simples"?
[<Inject>] ITypeResolver<'t>
como um argumento opcional de uma classe estática (somente bibliotecas altamente especializadas usam esse recurso, veja Fable.SimpleJson/Thoth.Json)Eu acho que o babel faz o tree-shake e remove as funções não utilizadas quando você faz o pacote de produção. Inlining iria derrotar isso.
@Luiz-Monad Você está dizendo que, idealmente, nada em Feliz deve ser embutido? Esse inlining por motivos de tamanho de pacote é contraproducente?
@Luiz-Monad O que você está dizendo seria incrível! Pelo menos se a compilação funcionasse dessa maneira. Aqui está um exemplo que você pode tentar com o REPL:
module App
type prop =
// does useless stuff
static member f() =
[ 1 .. 100 ]
|> List.map (fun x -> x * 20)
|> List.collect (fun n -> [n; n])
|> List.fold (+) 0
// does useless stuff
static member inline k() =
[ 1 .. 100 ]
|> List.map (fun x -> x * 20)
|> List.collect (fun n -> [n; n])
|> List.fold (+) 0
static member g() = 1
let value = prop.g()
printfn "%d" value
Onde prop
contém:
f()
contém um corpo de função -> não embutido e também não usadok()
contém um corpo de função -> embutido, mas não usadog()
contém uma função -> não inline e usadaVocê pensaria que f()
e g()
não serão compilados, mas esse não é o caso, f()
(não embutido e não usado) é compilado de qualquer maneira, mas k()
(inline, não usado) não faz parte do pacote compilado
import { fold, collect, map, ofSeq, ofArray } from "fable-library/List.js";
import { type } from "fable-library/Reflection.js";
import { rangeNumber } from "fable-library/Seq.js";
import { toConsole, printf } from "fable-library/String.js";
import { declare } from "fable-library/Types.js";
export const prop = declare(function App_prop() {});
export function prop$reflection() {
return type("App.prop");
}
export function prop$$$f() {
return fold(function folder(x$$1, y) {
return x$$1 + y;
}, 0, collect(function mapping$$1(n) {
return ofArray([n, n]);
}, map(function mapping(x) {
return x * 20;
}, ofSeq(rangeNumber(1, 1, 100)))));
}
export function prop$$$g() {
return 1;
}
export const value = prop$$$g();
toConsole(printf("%d"))(value);
Ele realmente funciona, mas você precisa configurar webpack
para fazer isso, porque o que faz o tree-shake não é a fábula em si, então não funcionará no REPL.
Antes de
/// Library.fs
module Library
type prop =
// does useless stuff
static member f() =
[ 1 .. 100 ]
|> List.map (fun x -> x * 20)
|> List.collect (fun n -> [n; n])
|> List.fold (+) 0
// does useless stuff
static member inline k() =
[ 1 .. 100 ]
|> List.map (fun x -> x * 20)
|> List.collect (fun n -> [n; n])
|> List.fold (+) 0
type AppMain =
static member g() = 1
//// App.fs
module App
let value = Library.AppMain.g ()
printfn "%d" value
Depois de
declare(function Library_prop() {
// see its empty, this weren't removed too because of `keep_classnames: true, keep_fnames: true ` in the terser plugin
});
declare(function Library_AppMain() {
});
!function toConsole(arg) {
return arg.cont(function (x) {
console.log(x)
})
}(printf('%d')) (1),
__webpack_require__.d(__webpack_exports__, 'value', function () {
return 1
})
Além disso, há uma ressalva para o seu teste, o arquivo de entrada é meio que especial, pois as funções dele não são removidas. (Eu acho que há algo a ver com a inicialização de classes estáticas, algo tem que chamar o construtor de inicializador estático para fazer efeitos colaterais nos módulos)
Veja este repositório que fiz para este teste https://github.com/Luiz-Monad/test-tree-shaking
Muito obrigado pelo repositório de exemplo! Eu definitivamente investigarei isso mais a fundo para ver se o inlining está realmente fazendo algo útil no contexto de Feliz
Eu definitivamente investigarei isso mais a fundo para ver se o inlining está realmente fazendo algo útil no contexto de Feliz
Legal, ansioso para ouvir o que você encontra :)
Fantástico! Eu definitivamente ficaria feliz em dar uma olhada se você
Por favor, confira a filial feliz
em cmeeren/fable-elmish-electron-material-ui-demo .
A maior parte do código é gerada automaticamente com base nos documentos da API (HTML). Há um projeto gerador (feio e hacky, mas faz o trabalho) e um projeto para as ligações reais. No projeto de renderização, apenas App.fs
foi convertido para usar as novas ligações no estilo Feliz.
Por favor, dê uma olhada quando você quiser, e deixe-me saber o que você pensa e se você tiver alguma dúvida.
@cmeeren Parece muito bom e muito melhor do que a API IMHO atual, mas ainda é um pouco difícil de ler, mas isso é mais a natureza da própria biblioteca, tem muitas partes muito específicas com as quais você deve se familiarizar. Acho que há partes que poderiam ser melhoradas, veja este exemplo:
Mui.appBar [
prop.className c.appBar
prop.appBar.position.fixed'
prop.children [
Mui.toolbar [
prop.children [
Mui.typography [
prop.typography.variant.h6
prop.typography.color.inherit'
prop.text (pageTitle model.Page)
]
]
]
]
]
Minha versão pessoal perfeita deste trecho seria transformá-lo no seguinte:
Mui.appBar [
AppBar.className c.appBar
AppBar.position.fixed'
AppBar.children [
Mui.toolbar [
Toolbar.children [
Mui.typography [
Typography.variant.h6
Typography.color.inherit'
Typygraphy.text (pageTitle model.Page)
]
]
]
]
]
Dessa forma, é fácil encontrar elementos Mui porque você pode apenas "pontilhar" Mui e uma vez que você encontrou seu elemento ( appBar
), você pode "pontilhar" o nome do módulo ( AppBar
) para definir as propriedades e tal
Talvez mantenha
AppBar
em minúsculas também
Acho que você entendeu, mas para ser mais exato, a sintaxe geral dessa API é a seguinte, onde {Element}
é um elemento react:
Mui.{element} [
{Element}.{propName}.{propValue}
{Element}.children [
Mui.{otherElem} [
{OtherElem}.{propName}.{propValue}
// etc.
]
]
]
O que você pensa sobre isso? Esta API também inspirou a biblioteca as ideias de elementos fabulosos-simples se você quiser ver uma implementação concreta
Eu acho que é perfeito, e exatamente o tipo de coisa que eu queria saber a sua opinião. Eu inicialmente escolhi ter tudo abaixo de prop
já que era assim que a biblioteca principal funcionava, mas é claro que você realmente não tem nenhum props específico de componente, enquanto o MUI tem mais mais menos apenas props específicos de componente.
Acho que ficar com nomes de módulos em letras minúsculas pode parecer melhor (e economizar um pressionamento de tecla extra), mas estou aberto a contra-argumentos.
Ainda bem que eu gerei essas coisas automaticamente, isso facilita a mudança.
Vou atualizar e te aviso.
Há uma coisa em particular que eu gostaria de obter opiniões, no entanto. É o material ClassName
. O gancho makeStyles
retorna um objeto com os mesmos adereços daquele que foi passado, mas onde cada elemento (JSS) foi substituído por uma string, que é o nome da classe a ser usada (e pode ser passado para, por exemplo, prop.className
).
Agora, não há como digitar isso em F#, então tenho que trabalhar com o que tenho. Normalmente todos os adereços do objeto de estilo são IStyleAttribute list
. Isso significa que posso adicionar uma sobrecarga para prop.className
que aceita IStyleAttribute list
, o que obviamente é uma mentira, pois em tempo de execução é uma string. Se você realmente passasse IStyleAttribute list
, falharia. Além de prop.className
, isso também vale para todos os classes.<element>.<classesName>
(usados em <element>.classes [ ... ]
). Eles também aceitam um nome de classe (string).
O que eu fiz, como você pode ver, é "exigir" que todas as propriedades IStyleAttribute list
do objeto de estilo sejam envolvidas em asClassName
, que basicamente apenas descompacta para IClassName
(um proxy para string
, se preferir). E então eu adicionei uma sobrecarga a prop.className
aceitando IClassName
, e fiz todos os adereços classes
aceitarem IClassName
. Gosto que seja mais fortemente tipado, mas não gosto que exija digitação extra ( asClassName
para cada regra CSS de nível superior). O compilador reclamará se você errar, mas não lhe dirá o que fazer, e ainda é um ruído extra.
Você tem alguma entrada sobre isso?
Além disso, notei isso:
f#
listItem.divider ((page = Home))
Aqui, parênteses duplos são necessários, pois, caso contrário, o F# o interpreta como tentando chamar listItem.divider
com o parâmetro $# page
2$#$ (inexistente) definido como Home
(em vez do value
parâmetro page = Home
). Você vê uma maneira de evitar isso?
Olá @cmeeren , antes de tudo, eu adoro essa sintaxe:
Mui.appBar [
prop.className c.appBar
appBar.position.fixed'
appBar.children [
Mui.toolbar [
toolbar.children [
Mui.typography [
typography.variant.h6
typography.color.inherit'
prop.text (pageTitle model.Page)
]
]
]
]
]
Parece tão limpo e tão simples! No entanto, se eu fosse você, provavelmente teria duplicado algumas das funções genéricas prop
em adereços específicos de componentes, como appBar.className
em vez de (ou ao lado) prop.className
para que todos eles parecem simétricos, mas o mais importante é fornecer a sobrecarga IClassName
ao componente específico do Mui em vez do prop.className
genérico que recebe uma string porque makeStyles
também é específico do Mui construir e faz sentido que ele se aplique apenas aos componentes Mui.
Acho que você abordou o makeStyles
da melhor maneira possível, pelo menos agora não consigo pensar em uma maneira melhor (embora eu não seja muito fã de asClassName
, talvez Styles.createClass
em vez disso? cabe a você).
Quanto a listItem.divider ((page = Home))
, é complicado, você pode adicionar uma função fictícia let when (x: bool) = x
mas isso é apenas ruído. Eu acho que é melhor arquivá-lo como um bug do compilador porque não consigo pensar no motivo pelo qual o compilador F # não pode resolver a sobrecarga adequada da função, eu não tentei, mas vou investigar quando o tempo permitir
Por fim, não sou tão receptivo como de costume esta semana porque estou de férias agora, então talvez não consiga ler/verificar tudo etc.
No entanto, se eu fosse você, provavelmente teria duplicado algumas das funções genéricas
prop
em adereços específicos de componentes, comoappBar.className
em vez de (ou ao lado)prop.className
para que todos eles parecem simétricos, mas o mais importante é fornecer a sobrecargaIClassName
ao componente específico do Mui em vez doprop.className
genérico que recebe uma string porquemakeStyles
também é específico do Mui construir e faz sentido que ele se aplique apenas aos componentes Mui.
Confira agora :) Fiz melhorias e expansões drásticas nos últimos dias, apenas empurrei (não concluído, estou passando por todos os componentes MUI para verificar e melhorar os adereços gerados).
Acho que você abordou o
makeStyles
da melhor maneira possível, pelo menos agora não consigo pensar em uma maneira melhor (embora eu não seja muito fã deasClassName
, talvezStyles.createClass
em vez disso? cabe a você).
Eu gostaria de mantê-lo o mais curto possível, pois será muito usado, mas estou aberto para outros nomes. Embora eu tenha metade da mente para apenas ter uma sobrecarga IStyleAttribute list
. Ele removerá potencialmente um pouco de ruído e duvido que seja muito perigoso, mesmo que tecnicamente possa ser usado incorretamente.
Quanto a
listItem.divider ((page = Home))
, é complicado, você pode adicionar uma função fictícialet when (x: bool) = x
mas isso é apenas ruído. Eu acho que é melhor arquivá-lo como um bug do compilador porque não consigo pensar no motivo pelo qual o compilador F # não pode resolver a sobrecarga adequada da função, eu não tentei, mas vou investigar quando o tempo permitir
Obrigado, eu registrei um problema agora: https://github.com/dotnet/fsharp/issues/7423
Por fim, não sou tão receptivo como de costume esta semana porque estou de férias agora, então talvez não consiga ler/verificar tudo etc.
Entendi, sem problemas. Continuarei postando problemas se me deparar com coisas, e você apenas responde no seu próprio tempo.
Confira agora :) Fiz melhorias e expansões drásticas nos últimos dias, apenas empurrei (não concluído, estou passando por todos os componentes MUI para verificar e melhorar os adereços gerados).
Parece muito bom, com os documentos gerados também 😍 talvez seja hora de colocar seu próprio repositório?
Eu gostaria de mantê-lo o mais curto possível, pois será muito usado, mas estou aberto para outros nomes. Embora eu tenha metade da mente para ter apenas uma sobrecarga de lista IStyleAttribute. Ele removerá potencialmente um pouco de ruído e duvido que seja muito perigoso, mesmo que tecnicamente possa ser usado incorretamente.
Isso também funcionaria, as bibliotecas Fable enganam o sistema de tipos o tempo todo;)
Obrigado, registrei um problema agora: dotnet/fsharp#7423
Impressionante! muito obrigado
Parece muito bom, com os documentos gerados também 😍 talvez seja hora de colocar seu próprio repositório?
Eu tenho pensado nisso, mas ainda existem alguns bugs importantes (por exemplo, #27) eu prefiro descobrir com a conveniência de ter tudo no mesmo lugar, então acho que vou mantê-lo lá até que esteja pronto para um pré-lançamento no nuget (espero que não seja muito longo).
@Zaid-Ajaj Estou quase terminando o Feliz.MaterialUI. Publicará em um repositório separado em breve. Seria ótimo que você revisasse, em primeiro lugar para verificar algumas decisões de design e garantir consistência com Feliz, mas também para verificar algumas coisas de implementação (por exemplo, estou usando coisas que você fará internamente ou há coisas que eu não estou usando do Feliz, mas deveria estar usando).
Quando eu crio o novo repositório, tudo bem se eu criar problemas explicando o que eu gostaria que fosse revisado e marcar você?
Agora carreguei um rascunho de Feliz.MaterialUI para cmeeren/Feliz.MaterialUI . Eu criei vários problemas com coisas que eu gostaria que fossem revisadas.
Eu ficaria muito grato se você pudesse encontrar tempo para dar uma olhada neles!
Não há necessidade de gastar muito tempo em cada questão; Eu realmente só quero uma segunda opinião pelas razões mencionadas no meu comentário anterior. Fico feliz em ir para as ervas daninhas, se você quiser, mas até mesmo "isso parece bom" seria ótimo.
Não há pressa, claro. :)
Trabalho incrível @cmereren! À primeira vista, as ligações parecem muito limpas, vou dar uma olhada em cada edição nos próximos dias, prometo :smile:
Ei! Alguma chance de continuar analisando os problemas? Novamente, sem pressa, apenas um lembrete amigável, já que não tenho notícias suas há algumas semanas 😃
Eu realmente olhei para os problemas, mas como eu disse antes, se uma API é boa ou não vem de casos de uso, é por isso que eu pedi que você publicasse uma versão alfa para que eu pudesse testá-la, mas não tive o tempo para fazê-lo ainda (foi na minha mente esta semana :smile:)
Olá @cmeeren , acho que podemos considerar isso resolvido, certo? Vou encerrar a questão, por favor, reabra para uma discussão mais aprofundada, se necessário
Comentários muito úteis
Olá @cmeeren , acho que podemos considerar isso resolvido, certo? Vou encerrar a questão, por favor, reabra para uma discussão mais aprofundada, se necessário